[译]ES6特性
作用域
使用let
/const
替代var
。
var
有什么错?
var
说明的变量会产生作用于提升的概念。
ES5:
var x = 'outer';
function test(inner) {
if (inner) {
var x = 'inner'; // scope whole function
return x;
}
return x; // gets redefined because line 4 declaration is hoisted
}
test(false); // undefined 😱
test(true); // inner
test(false)
返回的是undefined而不是outer。
为什么?
因为if-block没有执行,第四行的var x
被提升了。
提升后,上面的代码变成了下面的样子:
var x = 'outer';
function test(inner) {
var x; // 提升到了
if (inner) {
x = 'inner'; // 初始化没有提升
return x;
}
return x;
}
ES6中可以这样解决这个问题:
let x = 'outer';
function test(inner) {
if (inner) {
let x = 'inner';
return x;
}
return x; // gets result from line 1 as expected
}
test(false); // outer
test(true); // inner
将var
改为let
后,返回的就是如你所愿的outer了。如果if
block没有调用,x
变量也不会提升了。
IIFE
ES5:
{
var private = 1;
}
console.log(private); // 1
可以看到private
暴露出来了。为了不暴露它,你需要用IIFE包裹住它:
ES5
(function(){
var private2 = 1;
})();
console.log(private2); // Uncaught ReferenceError
如果你看过jQuery/lodash的源码,你会发现它们都使用了IIFE来避免全局变量污染。
在ES6中解决这个问题简单许多,不需要再使用IIFE了,我们只需要使用block和let
:
ES6:
{
let private3 = 1;
}
console.log(private3); // Uncaught ReferenceError
const
如果有个变量你永远都不想它改变的话,你可以使用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}.');
多行字符串
不再需要像下面一样使用 +\n
了。
ES5:
var template = '<li *ngFor="let todo of todos" [ngClass]="{completed: todo.isDone}" >\n' +
' <div class="view">\n' +
' <input class="toggle" type="checkbox" [checked]="todo.isDone">\n' +
' <label></label>\n' +
' <button class="destroy"></button>\n' +
' </div>\n' +
' <input class="edit" value="">\n' +
'</li>';
console.log(template);
在ES6中使用反引号来解决这个问题:
const template = `<li *ngFor="let todo of todos" [ngClass]="{completed: todo.isDone}" >
<div class="view">
<input class="toggle" type="checkbox" [checked]="todo.isDone">
<label></label>
<button class="destroy"></button>
</div>
<input class="edit" value="">
</li>`;
console.log(template);
解构赋值
获取一个数组中的元素
ES5:
var array = [1, 2, 3, 4, 5];
var first = array[0];
var third = array[2];
console.log(first, third); // 1 3
ES6:
const array = [1, 2, 3, 4, 5];
const [first, ,third] = array;
console.log(first, third); // 1 3
交换变量
ES5:
var a = 1;
var b = 2;
var temp = a;
a = b;
b = temp;
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
在ES6中,调用者可以从选择的选取返回数据(第6行):
ES6:
function margin() {
const left=1, right=2, top=3, bottom=4;
return { left, right, top, bottom };
}
const { left, bottom } = margin();
console.log(left, bottom); // 1 4
注意第3行,我们可以将{ 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中,我们不再使用构造函数Function,而是使用class
。
在ES5中,我们使用构造函数Function来实现OOP。
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
关键字来实现类。同时使用speak()
来替代constructor.prototype.speak = function() {}
。
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.
继承
在ES5中,使用原型链来实现继承。
ES5:
var Lion = (function () {
function MyConstructor(name){
Animal.call(this, name);
}
// prototypal inheritance
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提供了extends
和super
关键字。
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.
原生的Promise
ES5:
function printAfterTimeout(string, timeout, done){
setTimeout(function(){
done(string);
}, timeout);
}
printAfterTimeout('Hello ', 2e3, function(result){
console.log(result);
// nested callback
printAfterTimeout(result + 'Reader', 2e3, function(result){
console.log(result);
});
});
ES6:
function printAfterTimeout(string, timeout){
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(string);
}, timeout);
});
}
printAfterTimeout('Hello ', 2e3).then((result) => {
console.log(result);
return printAfterTimeout(result + 'Reader', 2e3);
}).then((result) => {
console.log(result);
});
箭头函数
在ES5中this
有些问题:
ES5:
var _this = this; // need to hold a reference
$('.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
需要使用一个临时变量或者bind
来绑定this。在ES6中,你可以使用箭头函数。
ES6:
// this will reference the outer one
$('.btn').click((event) => this.sendData());
// implicit returns
const ids = [291, 288, 984];
const messages = ids.map(value => `ID is ${value}`);
for of
ES6中提供了for of
ES5:
// for
var array = ['a', 'b', 'c', 'd'];
for (var i = 0; i < array.length; i++) {
var element = array[i];
console.log(element);
}
// forEach
array.forEach(function (element) {
console.log(element);
});
ES6:
// for ...of
const array = ['a', 'b', 'c', 'd'];
for (const element of array) {
console.log(element);
}
参数默认值
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 😱
point(0, 0, false) // 0 -1 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
Rest parameters
ES5:
function printf(format) {
var params = [].slice.call(arguments, 1);
console.log('params: ', params);
console.log('format: ', format);
}
printf('%s %d %.2f', 'adrian', 321, Math.PI);
ES6提供了...
操作符。
ES6:
function printf(format, ...params) {
console.log('params: ', params);
console.log('format: ', format);
}
printf('%s %d %.2f', 'adrian', 321, Math.PI);
展开操作符(Spread operator)
ES5:
Math.max.apply(Math, [2,100,1,6,43]) // 100
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:
const array1 = [2,100,1,6,43];
const array2 = ['a', 'b', 'c', 'd'];
const array3 = [false, true, null, undefined];
console.log([...array1, ...array2, ...array3]);