ECMAScript 6,也叫 ES6 或 ES2015。
JavaScript 自 1995 年面世,1997 年成立的 ECMAScript 引领着 JavaScript 的发展。已发布的版本 ES3、 ES5、 ES6 。
- JavaScript 进化史
ES3 与 ES5 之间隔了 10 年,而ES5 与 ES6 之间隔了 6 年。改进的新模式是每年都渐进式地做一些小改动,而不是像 ES6 一样一次性地进行大量的更改。
- 浏览器支持
大部分都已可以使用
ES6 新增优势
- 块级作用域变量let const
var 定义的变量会泄露到其它代码块,比如 for 循环或是 if 块。
(详见let和const语法)
-
IIFE
在ES5中
{
var private = 1;
}
console.log(private); // 1
private 会发生泄漏。需要使用 IIFE(立即执行函数表达式)将其包起来:
(function(){
var private2 = 1;
})();
console.log(private2); // Uncaught ReferenceError
jQuery等开源项目的源代码,它们都利用了 IIFE,以避免污染全局环境,而只在全局下定义 _、$或是 jQuery。
而在ES6中
{
let private3 = 1;
}
console.log(private3); // Uncaught ReferenceError
Const表示定义了一个常量,更改值,会报错
-
文本模板
ES5
var first = 'Adrian';
var last = 'Mejia';
console.log('Your name is ' + first + ' ' + last + '.');
ES6可以用 反引号 和字符串插值 ${}:
const first = 'Adrian';
const last = 'Mejia';
console.log(`Your name is ${first} ${last}.`);
多行字符串也同样可以使用反引号连接
ES6 解构非常简明并且好用
ES5
var array = [1, 2, 3, 4];
var first = array[0];
var third = array[2];
console.log(first, third); // 1 3
等同于:
ES6
const array = [1, 2, 3, 4];
const [first, ,third] = array;
console.log(first, third); // 1 3
- 调换值
ES5
var a = 1;
var b = 2;
var tmp = a;
a = b;
b = tmp;
console.log(a, b); // 2 1
等同于
ES6
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1
- 返回多个值
ES5
function margin() {
var left=1, right=2, top=3, bottom=4;
return { left: left, right: right, top: top, bottom: bottom };
}
var data = margin();
var left = data.left;
var bottom = data.bottom;
console.log(left, bottom); // 1 4
在第3行,也可以像这样用数组返回(并保存序列):
return [left, right, top, bottom];
但之后调用时需要考虑返回数据的顺序。
var left = data[0];
var bottom = data[3];
ES6 中调用时只会选择需要的数据(第 6 行):
1. function margin() {
2 const left=1, right=2, top=3, bottom=4;
3 return { left, right, top, bottom };
4 }
5 const { left, bottom } = margin();
6 console.log(left, bottom); // 1 4
注意:第3行用到了一些其它的 ES6 功能。可以将 { left: left } 简化为 { left }.
参数匹配解构
ES5
var user = {firstName: 'Adrian', lastName: 'Mejia'};
function getFullName(user) {
var firstName = user.firstName;
var lastName = user.lastName;
return firstName + ' ' + lastName;
}
console.log(getFullName(user)); // Adrian Mejia
ES6
const user = {firstName: 'Adrian', lastName: 'Mejia'};
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
console.log(getFullName(user)); // Adrian Mejia
深度匹配
ES5
function settings() {
return { display: { color: 'red' }, keyboard: { layout: 'querty'} };
}
var tmp = settings();
var displayColor = tmp.display.color;
var keyboardLayout = tmp.keyboard.layout;
console.log(displayColor, keyboardLayout); // red querty
ES6
function settings() {
return { display: { color: 'red' }, keyboard: { layout: 'querty'} };
}
const { display: { color: displayColor }, keyboard: { layout: keyboardLayout }} = settings();
console.log(displayColor, keyboardLayout); // red querty
使用数组解构获取元素或调换变量,这样就不用创建临时引用了。
对于多返回值的情况,不要用数组解构,用对象解构。
- 类和对象
ES6 用“类”替代“构造函数”。
在 JavaScript 中,每个对象都有原型对象。所有 JavaScript 对象都从原型上继承方法和属性。
ES5 以面向对象编程的方式创建对象,是利用构造函数实现的:
ES5
var Animal = (function () {
function MyConstructor(name) {
this.name = name;
}
MyConstructor.prototype.speak = function speak() {
console.log(this.name + ' makes a noise.');
};
return MyConstructor;
})();
var animal = new Animal('animal');
animal.speak(); // animal makes a noise.
ES6 可以用 class、constructor 等新的关键字、更少的样板代码实现相同的效果。同样可以看到相比于constructor.prototype.speak = function (),用 speak() 定义方法更加清晰:
ES6
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
const animal = new Animal('animal');
animal.speak(); // animal makes a noise.
用 class 语法,避免直接操作 prototype.
避免出现空的构造器。如果没有指明,类会有默认的构造器的。
- 继承
基于前面的 Animal 类,现在想要拓展 Animal,定义一个 Lion 类。
ES5
var Lion = (function () {
function MyConstructor(name){
Animal.call(this, name);
}
MyConstructor.prototype = Object.create(Animal.prototype);
MyConstructor.prototype.constructor = Animal;
MyConstructor.prototype.speak = function speak() {
Animal.prototype.speak.call(this);
console.log(this.name + ' roars ');
};
return MyConstructor;
})();
var lion = new Lion('Simba');
lion.speak(); // Simba makes a noise.
// Simba roars.
ES6
class Lion extends Animal {
speak() {
super.speak();
console.log(this.name + ' roars ');
}
}
const lion = new Lion('Simba');
lion.speak(); // Simba makes a noise.
// Simba roars.
使用内置的 extends 实现继承。
原生 Promise
用 promise 替代回调地狱
ES5
function printAfterTimeout(string, timeout, done){
setTimeout(function(){
done(string);
}, timeout);
}
printAfterTimeout('Hello ', 2000, function(result){
console.log(result);
// nested callback
printAfterTimeout(result + 'Reader', 2000, function(result){
console.log(result);
});
});
这个函数接收一个回调,在 done 后执行。我们想要先后执行两次,所以在回调中又一次调用了 printAfterTimeout。
如果需要多次回调。用 promise 实现
ES6
function printAfterTimeout(string, timeout){
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(string);
}, timeout);
});
}
printAfterTimeout('Hello ', 2000).then((result) => {
console.log(result);
return printAfterTimeout(result + 'Reader', 2000);
}).then((result) => {
console.log(result);
});
- 箭头函数
ES6 没有移除函数表达式,但是新增了箭头函数。
ES5 this的指向
var _this = this; //
$('.btn').click(function(event){
_this.sendData(); // reference outer this
});
$('.input').on('change',function(event){
this.sendData(); // reference outer this
}.bind(this)); // bind to outer this
在函数内,需要用临时变量指向 this 或者使用 bind 绑定。ES6 中可以使用箭头函数。
For…of
最开始用 for ,然后使用 forEach,而现在可以用 for…of:
ES6
$('.btn').click((event) => this.sendData());
// implicit returns
const ids = [291, 288, 984];
const messages = ids.map(value => `ID is ${value}`);
ES6 的 for…of 也可以用来迭代。
默认参数
之前需要检测变量是否定义了,而现在可以指定 default parameters 的值。
ES5
function point(x, y, isFlag){
x = x || 0;
y = y || -1;
isFlag = isFlag || true;
console.log(x,y, isFlag);
}
point(0, 0) // 0 -1 true//我们传的值是 0, 0 但是得到的是 0, -1
point(0, 0, false) // 0 -1 true//传进去 false 但是得到的是 true。
point(1) // 1 -1 true
point() // 0 -1 true
这可能是检测变量有值或指定默认值的惯用模式,但也存在一些问题:
ES6
function point(x = 0, y = -1, isFlag = true){
console.log(x,y, isFlag);
}
point(0, 0) // 0 0 true
point(0, 0, false) // 0 0 false
point(1) // 1 -1 true
point() // 0 -1 true
ES5 是因为先要检测 undefined的值,而 false、 null、 undefined 和 0 都是假的值。我们可以加些代码:
ES5
function point(x, y, isFlag){
x = x || 0;
y = typeof(y) === 'undefined' ? -1 : y;
isFlag = typeof(isFlag) === 'undefined' ? true : isFlag;
console.log(x,y, isFlag);
}
point(0, 0) // 0 0 true
point(0, 0, false) // 0 0 false
point(1) // 1 -1 true
point() // 0 -1 true
现在当检测 undefined 值时就符合我们的要求了。
之前使用 arguments,而现在可以用展开操作符。
现在可以用展开操作符 ... 达到相同的目的。
ES6
function printf(format, ...params) {
console.log('params: ', params);
console.log('format: ', format);
}
printf('%s %d %.2f', 'adrian', 321, Math.PI);
展开操作符...
之前用 apply(),现在可以方便地使用展开操作符 ... 了:
提示:apply() 可以将数组转化为一系列参数。例如 Math.max() 接收一系列参数,但如果想应用于数组的话可以用 apply 帮助实现。
如上所述,apply 可以将数组当作参数序列进行传递:
ES5
Math.max.apply(Math, [2,100,1,6,43]) // 100
ES6 可以用展开操作符:
ES6
```
Math.max(...[2,100,1,6,43]) // 100
concat 合并数组,现在也可以用展开操作符:
ES5
var array1 = [2,100,1,6,43];
var array2 = ['a', 'b', 'c', 'd'];
var array3 = [false, true, null, undefined];
console.log(array1.concat(array2, array3));
ES6 可以用展开操作符展开嵌套的数组:
ES6
>
const array1 = [2,100,1,6,43];
> const array2 = ['a', 'b', 'c', 'd'];
> const array3 = [false, true, null, undefined];
> console.log([...array1, ...array2, ...array3])