本文参考了阮一峰老师的ES6教程,部分示例代码来源于ECMAScript 6 入门。
块级作用域
ES6中针对块级作用域进行了一些规范化的定义。
-
使用
let
关键字来创建块级作用域变量,该方式声明的变量只在let
所在的代码块有效。-
比如
for
循环中的i
,就可以使用let
来声明,这样i
只在for
循环体内有效。 -
在
for
循环中,每一轮循环,i
都是一个新声明的变量,并且只在本次循环的代码块中生效。 -
注意
for
循环中,设置循环变量的那部分是父作用域,循环体内部是一个单独的子作用域,因此在for
循环的代码块中再次用let
声明i
会创建一个新的变量。for (let i = 0; i < 3; i++) { let i = 'abc'; console.log(i); } // abc // abc // abc //输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。
-
-
使用
const
关键字来创建块级作用域常量,常量在创建后不能被重新赋值。 -
不存在变量提升,如果在
let
声明之前使用或对该变量赋值,会报错ReferenceError
。 -
不允许在相同作用域内,重复声明同一个变量。
ES5规定中不允许在块级作用域中声明函数,但浏览器却可以这样做。而ES6中明确规定可以在块级作用域中声明函数,相当于
let
声明,同时为了兼容历史规则,允许浏览器有自己的行为。这样一来,在不同浏览器中,块级作用域声明函数的规则具有较大的差异。为了避免不必要的bug,我们在块级作用域中,要使用函数表达式,而不是函数声明语句。
模板字面量
模板字面量是允许嵌入表达式的字符串字面量,它是增强版的字符串,采用反引号(键盘上ESC下面的那个键)来标识。
-
可以用模板字面量来创建多行字符串,空格和换行都会被视为字符串的一部分。
let msg = ` <h2>hello<h2> <h2>world<h2> `.trim(); console.log(msg); //输出 <h2>hello<h2> <h2>world<h2>
-
在模板字面量中使用变量占位符
${变量}
。let name = "Tom", message = `Hello, ${name}.`; console.log(message); // "Hello, Tom." //占位符可以是运算符或者函数调用等构成表达式 function fn() { return "Hello World"; } `foo ${fn()} bar` // foo Hello World bar let count = 10, price = 0.25, message = `${count} items cost $${(count * price).toFixed(2)}.`; console.log(message); // "10 items cost $2.50."
函数的扩展
函数的默认参数:
允许在调用时没有值或undefined
被传入时使用指定的默认参数值。
function fun(name='world'){
return 'hello ' + b;
}
fun(); //"hello world"
函数的rest参数:
延展运算符和一个变量名搭配使用,生成一个数组,用于获取函数多余的参数:
let sum = (a, b, ...m)=>{
let total = 0;
for(var i of m){
total += i;
}
console.log(`total:${total}`);
}
sum(1,2,3,4); // total:7
箭头函数:
使用箭头函数可以创建语法更为简洁的函数。
箭头函数不会创建自己的this
参数,它将继承使用执行上下文的this
值。
const values = [0, 3, 2, 5, 7, 4, 8, 1];
//函数的参数为空或者不止一个时,必须使用括号
values.sort((v1, v2) => v1 - v2);
//当函数的语句不止一行时,必须给代码块加上大括号,并使用return语句返回
//等同于
values.sort((v1, v2) => {return v1 - v2;});
//如果直接返回一个对象,则需要给对象加上括号,否则会报错
let getTempItem = id => ({ id: id, name: "Temp" });
//箭头函数和变量解构一起使用
const full = ({ first, last }) => first + ' ' + last;
// 等同于
function full(person) {
return person.first + ' ' + person.last;
}
变量解构赋值及扩展运算符
三点运算符 ...
的应用:rest参数及扩展运算符。
rest参数在上面已经演示过,下面作为扩展运算符使用:
数组的解构赋值:
let [a, b, c] = [1, 2, 3];
console.log(...[1,2,3]);
// 1 2 3
// 用来实现concat的效果
let arr1=[1,2]; let arr2=[3,4];
console.log(...[...arr1, ...arr2]);
// 1 2 3 4
let [m, ...n] = [1,2,3,4];
console.log(n); //Array(3)
let [a,b,c] = 'ES6';
console.log(a,b,c); //E S 6
//字符串转化为数组
let arr = [...'ES6'];
console.log(arr); //Array(3)
Promise使用
以前我们通过callback函数和事件来实现异步操作,层层嵌套,金字塔式,使代码非常臃肿。
ES6提供了Promise对象,里面保存着某个未来才会结束的事件,从它可以获取异步操作的消息。
这里只介绍一下常见用法,详情请看阮一峰ES6详解。
-
一个利用Promise异步加载图片的例子:
let loadImageAsync(url) { //返回一个Promise实例对象 return new Promise(function(resolve, reject){ const image = new Image(); image.onload = function() { resolve(image); //resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功” //在异步操作成功时调用,并将异步操作的结果,作为参数传递出去 }; image.onerror = function { reject(new Error('Could not load image at' + url)); //reject函数的作用是,将Promise对象的状态从“未完成”变为“失败” //在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去 }; image.src = url; }); }; //使用then方法来为Promise实例添加状态改变时的回调函数 //第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。 loadImageAsync(url).then((res) => { document.body.appendChild(res); //此处还可以return一个新的Promise,后面继续指定then }).catch((err) => { console.log("rejected:", err); }); //catch用来捕获rejected状态,也可以把该回调写入then(resolve回调, rejected回调)
-
如果使用
then
方法,依次指定了多个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。 -
前一个回调函数可能返回一个
Promise
对象,后一个回调函数就会等待该Promise
对象状态发生变化,才会被调用。例如:
loadImageAsync(url_1).then((res)=>{ return loadInfo(url_2); }).then(function funcA(resA) { console.log("resolved: ", resA); }, function funcB(err){ console.log("rejected: ", err); });
第一个
then
方法指定的回调函数,返回的是另一个Promise
对象。这时,第二个then
方法指定的回调函数,就会等待这个新的Promise
对象状态发生变化。如果变为resolved
,就调用funcA
,如果状态变为rejected
,就调用funcB
。 -
如果要同时使用多个Promise,还可以用
Promise.all()
:Promise.all([checkLogin(), getUserInfo()]).then(([res1, res2]) => { console.log(res1, res2); });
ES6的模块化
之前已经在模块化规范里详细介绍过:ES6模块化规范。
后续新增特性
ES6新增的内容很多,大部分常用的不再赘述,有一些容易混淆需注意的,如:
-
Object.keys()(ES5新增)
- 返回对象所有的可枚举属性,不包括继承过来的属性或方法
- 注意:如果是数组,遍历时返回的是索引属性(即0、1、2…)
for...in(非新增)
是遍历一个对象的可枚举属性,包括继承过来的属性或方法
-
for...of(ES6新增)
遍历可迭代对象定义要迭代的数据
ES6以后,基本每年发布一次新的ES,所以特性增加的较少。
ES7新增
-
Array.prototype.includes()
判断数组是否包含指定值 -
指数操作符
**
ES8新增
-
async/await
让异步函数写得更加流畅自然 -
Object.values()
返回对象自身的所有属性的值的数组,不包括继承的值 -
Object.entries()
返回一个给定对象自身可枚举属性的键值对的数组,包含的键值对也是数组形式
不包括继承值 -
函数参数后允许添加逗号
主要是用于多人协作开发减少不必要的行变更