let和const命令
let和const类似于javascript中的var的使用,都是用来声明变量的,只是都存在各自的特殊用法。
在javascript中只有全局作用域和函数作用域,例如:
var name = 'one';//全局变量
while(true){
name = 'two';
console.log(name);//two,内层变量覆盖了外层变量
break;
}
console.log(name);//two,
不出意料,两次输出结果都是two。
而ES6带来的一个新特性:
- let:为javascript添加一个块级作用域
使用let声明的变量,只作用于使用了let命令的代码块:
var name = 'one';//全局变量
while(true){
let name = 'two';
console.log(name);//two,
break;
}
console.log(name);//one
- const :声明一个只读的常量。一旦声明,常量的值就不能改变
const PI = Math.PI;
console.log(PI); //3.141592653589793
PI = 666; //报错Module build failed: SyntaxError: "PI" is read-only
模板字符串
es6模板字符简直是开发者的福音,解决了ES5在字符串功能上的痛点。
基本的字符串格式化,将表达式嵌入字符串中进行拼接。用${}来界定。例如:
//es5
var name = 'Archer'
console.log('hello' + name)
//es6
const name = 'Archer'
console.log(`hello ${name}`) //hello Archer
在ES5时我们通过反斜杠()来做多行字符串或者字符串一行行拼接。ES6反引号(``)直接搞定。
// es5
var msg = "Hi \
Archer!
"
// es6
const template = `<div>
<span>hello Archer</span>
</div>`
对于字符串es6当然也提供了很多厉害的方法,列举几个常用的:
// includes:判断是否包含然后直接返回布尔值
let str = 'hahay'
console.log(str.includes('y')) // true
// repeat: 获取字符串重复n次
let s = 'ha'
console.log(s.repeat(3)) // 'hahaha'
箭头函数
ES6很有意思的一部分就是函数的快捷写法,也就是箭头函数,箭头函数最直观的三个特点:
- 不需要function关键字来创建函数
- 省略return关键字
- 继承当前上下文的 this 关键字
let arr = [1,2,3];
arr.map(item => item + 1)
//等同于
arr.map(function(item){
return item + 1
})
当函数有且仅有一个参数的时候,是可以省略掉(),当函数返回有且仅有一个表达式的时候可以省略{};例如:
//参数name就没有括号
var people = name => 'hello' + name
函数的默认值
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。例如:
function log(x, y) {
y = y || 'Archer';
console.log(x, y);
}
log('Hello') // Hello Archer
log('Hello', 'China') // Hello China
log('Hello', '') // Hello Archer
上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为Archer。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。
现在 ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面,为赋值提供了简便的方法。例如:
function log(x, y = 'Archer') {
console.log(x, y);
}
log('Hello') // Hello Archer
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
可以看到,ES6 的写法比 ES5 简洁许多,而且非常自然。下面是另一个例子。
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
const p = new Point();
p // { x: 0, y: 0 }
展开运算符
...展开运算符,有两种层面
- 展开运算符(spread operator)作用是和字面意思一样,就是把东西展开。可以用在array和object上都行。例如:
let a = [1,2,3];
let b = [0, ...a, 4]; // [0,1,2,3,4]
let obj = { a: 1, b: 2 };
let obj2 = { ...obj, c: 3 }; // { a:1, b:2, c:3 }
let obj3 = { ...obj, a: 3 }; // { a:3, b:2 }
- 剩余操作符(rest operator)是解构的一种,意思就是把剩余的东西放到一个array里面赋值给它。一般只针对array的解构。例如:
let a = [1,2,3];
let [b, ...c] = a;
b; // 1
c; // [2,3]
// 也可以
let a = [1,2,3];
let [b, ...[c,d,e]] = a;
b; // 1
c; // 2
d; // 3
e; // undefined
// 也可以
function test(a, ...rest){
console.log(a); // 1
console.log(rest); // [2,3]
}
test(1,2,3)
对象的扩展
对象初始化简写
ES5我们对于对象都是以键值对的形式书写,是有可能出现键值对重名的。例如:
function people(name, age) {
return {
name: name,
age: age
};
}
键值对重名,ES6可以简写如下:
function people(name, age) {
return {
name,
age
};
}
ES6 同样改进了为对象字面量方法赋值的语法。ES5为对象添加方法:
const people = {
name: 'lux',
getName: function() {
console.log(this.name)
}
}
ES6通过省略冒号与 function 关键字,将这个语法变得更简洁:
const people = {
name: 'lux',
getName () {
console.log(this.name)
}
}
ES6 对象提供了Object.assign()这个方法来实现浅复制。Object.assign()可以把任意多个源对象自身可枚举的属性拷贝给目标对象,然后返回目标对象。第一参数即为目标对象。在实际项目中,我们为了不改变源对象。一般会把目标对象传为{}。
const obj = Object.assign({}, objA, objB)
解构
数组和对象是JS中最常用也是最重要表示形式。为了简化提取信息,ES6新增了解构,这是将一个数据结构分解为更小的部分的过程。
ES5我们提取对象中的信息形式如下:
const people = {
name: 'Archer',
age: 28
}
const name = people.name
const age = people.age
console.log(name + ' --- ' + age) //"Archer --- 28"
在ES6之前我们就是这样获取对象信息的,一个一个获取。现在,解构能让我们从对象或者数组里取出数据存为变量。例如:
//对象
const people = {
name: 'Archer',
age: 28
}
const { name, age } = people
console.log(`${name} --- ${age}`)
//数组
const color = ['red', 'blue']
const [first, second] = color
console.log(first) //'red'
console.log(second) //'blue'
import 和 export
import导入模块、export导出模块
//全部导入
import people from './example'
//有一种特殊情况,即允许你将整个模块当作单一对象进行导入
//该模块的所有导出都会作为对象的属性存在
import * as example from "./example.js"
console.log(example.name)
console.log(example.age)
console.log(example.getName())
//导入部分
import {name, age} from './example'
// 导出默认, 有且只有一个默认
export default App
// 部分导出
export class App extend Component {};
导入的时候有没有大括号的区别是什么。下面是我在工作中的总结:
- 当用export default people导出时,就用 import people 导入(不带大括号)
- 一个文件里,有且只能有一个export default。但可以有多个export。
- 当用export name 时,就用import { name }导入(记得带上大括号)
- 当一个文件里,既有一个export default people, 又有多个export name 或者 export
age时,导入就用 import people, { name, age } - 当一个文件里出现n多个 export 导出很多模块,导入时除了一个一个导入,也可以用import * as example
Promise
在promise之前代码过多的回调或者嵌套,可读性差、耦合度高、扩展性低。通过Promise机制,扁平化的代码机构,大大提高了代码可读性;用同步编程的方式来编写异步代码,保存线性的代码逻辑,极大的降低了代码耦合性而提高了程序的可扩展性。
Promise对象有以下两个特点:
- 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为
resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
说白了就是用同步的方式去写异步代码,下面代码创造了一个Promise实例:
const promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
下面是一个Promise对象的简单例子,timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(ms参数)以后,Promise实例的状态变为resolved,就会触发then方法绑定的回调函数。:
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}
timeout(100).then((value) => {
console.log(value);
});
Generators
生成器( generator)是能返回一个迭代器的函数。生成器函数也是一种函数,最直观的表现就是比普通的function多了个星号*,在其函数体内可以使用yield关键字,有意思的是函数会在每个yield后暂停。
这里生活中有一个比较形象的例子。咱们到银行办理业务时候都得向大厅的机器取一张排队号。你拿到你的排队号,机器并不会自动为你再出下一张票。也就是说取票机“暂停”住了,直到下一个人再次唤起才会继续吐票。
说说迭代器。当你调用一个generator时,它将返回一个迭代器对象。这个迭代器对象拥有一个叫做next的方法来帮助你重启generator函数并得到下一个值。next方法不仅返回值,它返回的对象具有两个属性:done和value。value是你获得的值,done用来表明你的generator是否已经停止提供值。继续用刚刚取票的例子,每张排队号就是这里的value,打印票的纸是否用完就这是这里的done。
// 生成器
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
// 生成器能像正规函数那样被调用,但会返回一个迭代器
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
那生成器和迭代器又有什么用处呢?
围绕着生成器的许多兴奋点都与异步编程直接相关。异步调用对于我们来说是很困难的事,我们的函数并不会等待异步调用完再执行,你可能会想到用回调函数,(当然还有其他方案比如Promise比如Async/await)。
生成器可以让我们的代码进行等待。就不用嵌套的回调函数。使用generator可以确保当异步调用在我们的generator函数运行一下行代码之前完成时暂停函数的执行。
那么问题来了,咱们也不能手动一直调用next()方法,你需要一个能够调用生成器并启动迭代器的方法。就像这样子的:
function run(taskDef) { //taskDef即一个生成器函数
// 创建迭代器,让它在别处可用
let task = taskDef();
// 启动任务
let result = task.next();
// 递归使用函数来保持对 next() 的调用
function step() {
// 如果还有更多要做的
if (!result.done) {
result = task.next();
step();
}
}
// 开始处理过程
step();
}
生成器与迭代器最有趣、最令人激动的方面,或许就是可创建外观清晰的异步操作代码。你不必到处使用回调函数,而是可以建立貌似同步的代码,但实际上却使用 yield 来等待异步操作结束。
总结
ES6的特性远不止于此,但对于我们日常的开发开说,这已经是够够的了,还有很多有意思的方法。比如for of,Iterator...等等。
来都来了点一下赞吧,你的赞是对我最大的鼓励^_^
希望更全面了解es6伙伴们可以去看阮一峰所著的电子书ECMAScript 6入门
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。