ES6特性歸納
ES的全稱是ECMAScript宵呛,它是JavaScript的規(guī)格藕坯,JS是ES的一種實(shí)現(xiàn)界睁。ES還有JScript(IE中實(shí)際使用的是這種腳本語(yǔ)言)尸闸,ActionScript(Flash中使用的腳本語(yǔ)言)等實(shí)現(xiàn)形式彻亲。本篇文章是對(duì)阮一峰-《ECMAScript 6入門》中JS新增特性的歸納孕锄。對(duì)于ES6對(duì)于原先JS api的擴(kuò)展請(qǐng)看ES6擴(kuò)展歸納一文
let和const
let只在塊級(jí)作用域內(nèi)有效吮廉,const聲明的變量的值不能被改變
for循環(huán)還有一個(gè)特別之處,就是循環(huán)語(yǔ)句部分是一個(gè)父作用域畸肆,而循環(huán)體內(nèi)部是一個(gè)單獨(dú)的子作用域宦芦。let在父作用域和子作用域互不影響。
let與const都不允許重復(fù)聲明轴脐。
變量的解構(gòu)賦值
用于將值從數(shù)組或?qū)傩詮膶?duì)象提取到不同的變量中调卑,一次給多個(gè)變量賦值的一種語(yǔ)法
數(shù)組解構(gòu)賦值
完全解構(gòu)賦值(左邊與右邊的量相等抡砂,或者大于右邊的量。即右邊的值被使用完全)
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
不完全解構(gòu)賦值(即等號(hào)左邊的模式恬涧,只匹配一部分的等號(hào)右邊的數(shù)組)
let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
默認(rèn)值
let [foo = true] = [];
foo // true
//必須為空或者undefined默認(rèn)值才生效
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
let [x, y = 'b'] = ['a', null]; // x='a', y=null
等號(hào)右邊的值注益,需要對(duì)象或轉(zhuǎn)為對(duì)象以后具備 Iterator 接口
對(duì)象的解構(gòu)賦值
根據(jù)左右兩邊的屬性名進(jìn)行賦值,變量名默認(rèn)為屬性名溯捆,如果屬性有值則變量名為屬性值
let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
var { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
嵌套解構(gòu)的對(duì)象解構(gòu)賦值
//loc,start是模式不是變量不會(huì)被賦值
var node = {
loc: {
start: {
line: 1,
column: 5
}
}
};
var { loc: { start: { line }} } = node;
line // 1
loc // error: loc is undefined
start // error: start is undefined
// 報(bào)錯(cuò),因?yàn)閒oo為undefined無(wú)法取得bar
let {foo: {bar}} = {baz: 'baz'};
對(duì)數(shù)組進(jìn)行對(duì)象屬性的解構(gòu)
let arr = [1, 2, 3];
//方括號(hào)這種寫法丑搔,屬于“屬性名表達(dá)式”
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
等號(hào)右邊的值,需要對(duì)象或轉(zhuǎn)為對(duì)象后進(jìn)行結(jié)構(gòu)賦值
函數(shù)解構(gòu)賦值
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
為函數(shù)變量指定默認(rèn)值提揍,當(dāng)x或y沒(méi)有值時(shí)啤月,就會(huì)啟用默認(rèn)值
function move({x = 0, y = 0} = {}) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
為函數(shù)參數(shù)指定默認(rèn)值,默認(rèn)值是整體{x,y}的劳跃,所以只要有參數(shù)就不啟用默認(rèn)值
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
Symbol
ES6引入了一種新的原始數(shù)據(jù)類型Symbol谎仲,表示獨(dú)一無(wú)二的值。
//不能有new,因?yàn)镾ymbol也是一種基礎(chǔ)類型
//參數(shù)字符串只用與描述,同參情況下也不相等
var s1 = Symbol('foo');
var s2 = Symbol('bar');
s1 // Symbol(foo)
s2 // Symbol(bar)
s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"
// 沒(méi)有參數(shù)的情況
var s1 = Symbol();
var s2 = Symbol();
s1 === s2 // false
Symbol作為屬性名
Symbol值作為對(duì)象屬性名時(shí)刨仑,不能用點(diǎn)運(yùn)算符郑诺。使用symbol保證屬性不會(huì)被覆蓋,symbol作為屬性名不會(huì)被常規(guī)的方法遍歷到杉武,可以使用該特性為對(duì)象定義內(nèi)部方法间景。
var mySymbol = Symbol();
// 第一種寫法
var a = {};
a[mySymbol] = 'Hello!';
// 第二種寫法
var a = {
[mySymbol]: 'Hello!'
};
// 第三種寫法
var a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
獲取Symbol屬性
通過(guò)Object.getOwnPropertySymbols方法獲取指定對(duì)象的所有Symbol屬性名。
var obj = {};
var a = Symbol('a');
var b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';
var objectSymbols = Object.getOwnPropertySymbols(obj);
objectSymbols
// [Symbol(a), Symbol(b)]
使用Reflect.ownKeys返回類型所有鍵名
let obj = {
[Symbol('my_key')]: 1,
enum: 2,
nonEnum: 3
};
Reflect.ownKeys(obj)
// ["enum", "nonEnum", Symbol(my_key)]
Symbol API
Symbol.for()與Symbol()這兩種寫法艺智,都會(huì)生成新的Symbol倘要。它們的區(qū)別是,前者會(huì)被登記在全局環(huán)境中供搜索十拣,后者不會(huì)封拧。
var s1 = Symbol.for('foo');
var s2 = Symbol.for('foo');
s1 === s2 // true
Symbol.keyFor方法返回一個(gè)已登記的 Symbol 類型值的key
var s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
var s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined
Symbol.for為Symbol值登記的名字,是全局環(huán)境的夭问,可以在不同的 iframe 或 service worker 中取到同一個(gè)值泽西。
對(duì)象有些內(nèi)置的Symbol值,如:對(duì)象的Symbol.hasInstance屬性缰趋,指向一個(gè)內(nèi)部方法捧杉。
class MyClass {
[Symbol.hasInstance](foo) {
return foo instanceof Array;
}
}
[1, 2, 3] instanceof new MyClass() // true
其他還有:Symbol.iterator,Symbol.replace,Symbol.search,Symbol.split秘血,Symbol.match指向?qū)ο竽J(rèn)的同名方法
Set和Map數(shù)據(jù)結(jié)構(gòu)
Set與數(shù)組
//set與數(shù)組互換
var set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]
set.size//4
// 去除數(shù)組的重復(fù)成員
[...new Set(array)]
Set中NaN可以等于自身
求交味抖、并和差集
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
在遍歷操作中,同步改變?cè)瓉?lái)的Set結(jié)構(gòu)灰粮,目前沒(méi)有直接的方法仔涩,但有兩種變通方法。
// 方法一
let set = new Set([1, 2, 3]);
set = new Set([...set].map(val => val * 2));
// set的值是2, 4, 6
// 方法二
let set = new Set([1, 2, 3]);
set = new Set(Array.from(set, val => val * 2));
// set的值是2, 4, 6
Set實(shí)例的屬性和方法
- Set.prototype.constructor:構(gòu)造函數(shù)粘舟,默認(rèn)就是Set函數(shù)熔脂。
- Set.prototype.size:返回Set實(shí)例的成員總數(shù)佩研。
- add(value):添加某個(gè)值,返回Set結(jié)構(gòu)本身霞揉。
- delete(value):刪除某個(gè)值旬薯,返回一個(gè)布爾值,表示刪除是否成功适秩。
- has(value):返回一個(gè)布爾值袍暴,表示該值是否為Set的成員。
- clear():清除所有成員隶症,沒(méi)有返回值政模。
WeakSet
WeakSet中的對(duì)象都是弱引用,即垃圾回收機(jī)制不考慮WeakSet對(duì)該對(duì)象的引用蚂会,也就是說(shuō)淋样,如果其他對(duì)象都不再引用該對(duì)象,那么垃圾回收機(jī)制會(huì)自動(dòng)回收該對(duì)象所占用的內(nèi)存胁住,不考慮該對(duì)象還存在于WeakSet之中
//只能添加對(duì)象
var ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set
//數(shù)組的成員只能是對(duì)象
var a = [[1,2], [3,4]];
var ws = new WeakSet(a);
//非對(duì)象時(shí)會(huì)報(bào)錯(cuò)
var b = [3, 4];
var ws = new WeakSet(b);
// Uncaught TypeError: Invalid value used in weak set(…)
- WeakSet.prototype.add(value):向WeakSet實(shí)例添加一個(gè)新成員趁猴。
- WeakSet.prototype.delete(value):清除WeakSet實(shí)例的指定成員。
- WeakSet.prototype.has(value):返回一個(gè)布爾值彪见,表示某個(gè)值是否在
WeakSet不能遍歷儡司,是因?yàn)槌蓡T都是弱引用,隨時(shí)可能消失余指,遍歷機(jī)制無(wú)法保證成員的存在捕犬,很可能剛剛遍歷結(jié)束,成員就取不到了酵镜。WeakSet的一個(gè)用處碉碉,是儲(chǔ)存DOM節(jié)點(diǎn),而不用擔(dān)心這些節(jié)點(diǎn)從文檔移除時(shí)淮韭,會(huì)引發(fā)內(nèi)存泄漏垢粮。
Map
map與數(shù)組
數(shù)組
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
new Map([[true, 7], [{foo: 3}, ['abc']]])
// Map {true => 7, Object {foo: 3} => ['abc']}
WeakMap
它只接受對(duì)象作為鍵名(null除外),不接受其他類型的值作為鍵名靠粪,而且鍵名所指向的對(duì)象蜡吧,不計(jì)入垃圾回收機(jī)制。
典型應(yīng)用是占键,一個(gè)對(duì)應(yīng)DOM元素的WeakMap結(jié)構(gòu)昔善,當(dāng)某個(gè)DOM元素被清除,其所對(duì)應(yīng)的WeakMap記錄就會(huì)自動(dòng)被移除捞慌∫唬基本上,WeakMap的專用場(chǎng)合就是啸澡,它的鍵所對(duì)應(yīng)的對(duì)象袖订,可能會(huì)在將來(lái)消失。WeakMap結(jié)構(gòu)有助于防止內(nèi)存泄漏嗅虏。
weakmap除了像weakset一樣可以用作存放dom節(jié)點(diǎn)外洛姑,還能夠用于部署私有屬性
let _counter = new WeakMap();
let _action = new WeakMap();
class Countdown {
constructor(counter, action) {
_counter.set(this, counter);
_action.set(this, action);
}
dec() {
let counter = _counter.get(this);
if (counter < 1) return;
counter--;
_counter.set(this, counter);
if (counter === 0) {
_action.get(this)();
}
}
}
let c = new Countdown(2, () => console.log('DONE'));
c.dec()
c.dec()
// DONE
遍歷方法
Set和Map都支持keys(),values(),entries(),forEach()等遍歷方式,對(duì)于set,keys()皮服、values()楞艾、entries()三種遍歷方式一模一樣。forEach()遍歷方式接受一個(gè)回調(diào)函數(shù)其中的兩個(gè)參數(shù)分別為集合中的元素以及序號(hào)(表示當(dāng)前是第幾項(xiàng))龄广。
Proxy
Proxy 用于修改某些操作的默認(rèn)行為硫眯,等同于在語(yǔ)言層面做出修改,所以屬于一種“元編程”(meta programming)择同,即對(duì)編程語(yǔ)言進(jìn)行編程两入。
var proxy = new Proxy({}, {
/*
target:目標(biāo)對(duì)象
property:訪問(wèn)的屬性
*/
get: function(target, property) {
return 35;
}
});
proxy.time // 35
proxy.name // 35
proxy.title // 35
可攔截操作
- get(target, propKey, receiver) 攔截取值操作
- set(target, propKey, value, receiver) 攔截設(shè)值操作
- has(target, propKey) 攔截propKey in proxy操作
- deleteProperty(target, propKey) 攔截delete proxy[propKey]的操作
- ownKeys(target) 攔截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)敲才、Object.keys(proxy)裹纳,返回一個(gè)數(shù)組。
- getOwnPropertyDescriptor(target, propKey) 攔截Object.getOwnPropertyDescriptor(proxy, propKey)
- defineProperty(target, propKey, propDesc) 攔截Object.defineProperty(proxy, propKey, propDesc)紧武、Object.defineProperties(proxy, propDescs)
- preventExtensions(target) 攔截Object.preventExtensions(proxy)
- getPrototypeOf(target) 攔截Object.getPrototypeOf(proxy)
- isExtensible(target) 攔截Object.isExtensible(proxy)
- setPrototypeOf(target, proto) 攔截Object.setPrototypeOf(proxy, proto)
- apply(target, object, args) 攔截 Proxy 實(shí)例(即target函數(shù)的調(diào)用)作為函數(shù)調(diào)用的操作剃氧,比如proxy(…args)、proxy.call(object, …args)阻星、proxy.apply(…)朋鞍、Reflect.apply(proxy, null,args)
- construct(target, args) 攔截 Proxy 實(shí)例作為構(gòu)造函數(shù)調(diào)用的操作,比如new proxy(…args)妥箕。必須返回一個(gè)對(duì)象否則會(huì)報(bào)錯(cuò)
Reflect
讓Object操作都變成函數(shù)行為番舆。某些Object操作是命令式,比如name in obj和delete obj[name]矾踱,而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)讓它們變成了函數(shù)行為恨狈。
// 老寫法
'assign' in Object // true
// 新寫法
Reflect.has(Object, 'assign') // true
Reflect對(duì)象的方法與Proxy對(duì)象的方法一一對(duì)應(yīng),只要是Proxy對(duì)象的方法呛讲,就能在Reflect對(duì)象上找到對(duì)應(yīng)的方法禾怠。這就讓Proxy對(duì)象可以方便地調(diào)用對(duì)應(yīng)的Reflect方法,完成默認(rèn)行為贝搁,作為修改行為的基礎(chǔ)吗氏。也就是說(shuō),不管Proxy怎么修改默認(rèn)行為雷逆,你總可以在Reflect上獲取默認(rèn)行為弦讽。
Promise對(duì)象
Promise,簡(jiǎn)單說(shuō)就是一個(gè)容器,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果往产。從語(yǔ)法上說(shuō)被碗,Promise 是一個(gè)對(duì)象,從它可以獲取異步操作的消息仿村。
Promise特點(diǎn):
- 對(duì)象的狀態(tài)不受外界影響锐朴。Promise對(duì)象代表一個(gè)異步操作,有三種狀態(tài):Pending(進(jìn)行中)蔼囊、Resolved(已完成焚志,又稱 Fulfilled)和Rejected(已失敗)畏鼓。只有異步操作的結(jié)果酱酬,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無(wú)法改變這個(gè)狀態(tài)
- 一旦狀態(tài)改變云矫,就不會(huì)再變膳沽,任何時(shí)候都可以得到這個(gè)結(jié)果。
用法
//兩個(gè)函數(shù)resolve和reject由JS引擎提供不用自己部署泼差。
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
//可以用then方法分別指定Resolved狀態(tài)和Reject狀態(tài)的回調(diào)函數(shù)贵少。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
Promise.all方法用于將多個(gè)Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例堆缘。
var p = Promise.all([p1, p2, p3]);
Promise.race只要p1滔灶、p2、p3之中有一個(gè)實(shí)例率先改變狀態(tài)吼肥,p的狀態(tài)就跟著改變录平。那個(gè)率先改變的 Promise 實(shí)例的返回值,就傳遞給p的回調(diào)函數(shù)缀皱。
var p = Promise.race([p1, p2, p3]);
Promise.resolve將現(xiàn)有對(duì)象轉(zhuǎn)為Promise對(duì)象斗这,Promise.resolve方法就起到這個(gè)作用。
Promise.resolve('foo')
// 等價(jià)于
new Promise(resolve => resolve('foo'))
兩個(gè)有用的方法
done()Promise對(duì)象的回調(diào)鏈啤斗,不管以then方法或catch方法結(jié)尾表箭,要是最后一個(gè)方法拋出錯(cuò)誤,都有可能無(wú)法捕捉到(因?yàn)镻romise內(nèi)部的錯(cuò)誤不會(huì)冒泡到全局)钮莲。因此免钻,我們可以提供一個(gè)done方法,總是處于回調(diào)鏈的尾端崔拥,保證拋出任何可能出現(xiàn)的錯(cuò)誤极舔。
Promise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected)
.catch(function (reason) {
// 拋出一個(gè)全局錯(cuò)誤
setTimeout(() => { throw reason }, 0);
});
};
finally方法用于指定不管Promise對(duì)象最后狀態(tài)如何,都會(huì)執(zhí)行的操作链瓦。它與done方法的最大區(qū)別拆魏,它接受一個(gè)普通的回調(diào)函數(shù)作為參數(shù),該函數(shù)不管怎樣都必須執(zhí)行。
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
Generator函數(shù)
Generator 函數(shù)是一個(gè)狀態(tài)機(jī)渤刃,封裝了多個(gè)內(nèi)部狀態(tài)拥峦。執(zhí)行 Generator 函數(shù)會(huì)返回一個(gè)遍歷器對(duì)象,可以依次遍歷 Generator 函數(shù)內(nèi)部的每一個(gè)狀態(tài)溪掀。
基本使用
每次執(zhí)行函數(shù)事镣,會(huì)從yield返回對(duì)應(yīng)的值
function* f() {
for(var i = 0; true; i++) {
var reset = yield i;
if(reset) { i = -1; }
}
}
var g = f();
g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
//next如果帶參數(shù)步鉴,則yield會(huì)返回相應(yīng)的參數(shù)
g.next(true) // { value: 0, done: false }
Generator.prototype.throw()
Generator函數(shù)返回的遍歷器對(duì)象揪胃,都有一個(gè)throw方法,可以在函數(shù)體外拋出錯(cuò)誤氛琢,然后在Generator函數(shù)體內(nèi)捕獲喊递。捕獲后會(huì)順帶執(zhí)行下一條yield,即執(zhí)行一次next方法
var g = function* () {
try {
yield;
} catch (e) {
console.log('內(nèi)部捕獲', e);
}
};
var i = g();
i.next();
try {
i.throw('a');
i.throw('b');
} catch (e) {
console.log('外部捕獲', e);
}
// 內(nèi)部捕獲 a
// 外部捕獲 b
一旦Generator執(zhí)行過(guò)程中拋出錯(cuò)誤阳似,且沒(méi)有被內(nèi)部捕獲骚勘,就不會(huì)再執(zhí)行下去了。
Generator.prototype.return()
return方法撮奏,可以返回給定的值俏讹,并且終結(jié)遍歷Generator函數(shù)。如果Generator函數(shù)內(nèi)部有try…finally代碼塊畜吊,那么return方法會(huì)推遲到finally代碼塊執(zhí)行完再執(zhí)行泽疆。
function* numbers () {
yield 1;
try {
yield 2;
yield 3;
} finally {
yield 4;
yield 5;
}
yield 6;
}
var g = numbers()
g.next() // { done: false, value: 1 }
g.next() // { done: false, value: 2 }
g.return(7) // { done: false, value: 4 }
g.next() // { done: false, value: 5 }
g.next() // { done: true, value: 7 }
yield* 語(yǔ)句
yield*語(yǔ)句,用來(lái)在一個(gè) Generator 函數(shù)里面執(zhí)行另一個(gè) Generator 函數(shù)玲献。yield后面的Generator函數(shù)(沒(méi)有return語(yǔ)句時(shí))殉疼,不過(guò)是for…of的一種簡(jiǎn)寫形式,完全可以用后者替代前者捌年。
function* foo() {
yield 'a';
yield 'b';
}
function* bar() {
yield 'x';
yield* foo();
yield 'y';
}
// 等同于
function* bar() {
yield 'x';
for (let v of foo()) {
yield v;
}
yield 'y';
}
任何數(shù)據(jù)結(jié)構(gòu)只要有Iterator接口瓢娜,就可以被yield*遍歷。
注意點(diǎn)
Generator函數(shù)總是返回一個(gè)遍歷器礼预,ES6規(guī)定這個(gè)遍歷器是Generator函數(shù)的實(shí)例眠砾,也繼承了Generator函數(shù)的prototype對(duì)象上的方法。
作為對(duì)象屬性時(shí)generator的寫法
let obj = {
* myGeneratorMethod() {
···
}
};
//等價(jià)于
let obj = {
myGeneratorMethod: function* () {
// ···
}
};
使得Generator能夠使用this:
function* F() {
this.a = 1;
yield this.b = 2;
yield this.c = 3;
}
var f = F.call(F.prototype);
f.next(); // Object {value: 2, done: false}
f.next(); // Object {value: 3, done: false}
f.next(); // Object {value: undefined, done: true}
f.a // 1
f.b // 2
f.c // 3
使得Generator能夠使用new:
function* gen() {
this.a = 1;
yield this.b = 2;
yield this.c = 3;
}
function F() {
return gen.call(gen.prototype);
}
var f = new F();
f.next(); // Object {value: 2, done: false}
f.next(); // Object {value: 3, done: false}
f.next(); // Object {value: undefined, done: true}
f.a // 1
f.b // 2
f.c // 3
Generator函數(shù)被稱為“半?yún)f(xié)程”(semi-coroutine)托酸,意思是只有Generator函數(shù)的調(diào)用者褒颈,才能將程序的執(zhí)行權(quán)還給Generator函數(shù)。
除了for…of循環(huán)以外获高,擴(kuò)展運(yùn)算符(…)哈肖、解構(gòu)賦值和Array.from方法內(nèi)部調(diào)用的,都是遍歷器接口念秧。
async
async 函數(shù)就是 Generator 函數(shù)的語(yǔ)法糖淤井。async函數(shù)就是將 Generator 函數(shù)的星號(hào)(*)替換成async,將yield替換成await,僅此而已币狠。
//使用Generator函數(shù)實(shí)現(xiàn)異步
var fs = require('fs');
var readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) reject(error);
resolve(data);
});
});
};
var gen = function* () {
var f1 = yield readFile('/etc/fstab');
var f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
//使用async實(shí)現(xiàn)異步
var asyncReadFile = async function () {
var f1 = await readFile('/etc/fstab');
var f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
async函數(shù)對(duì) Generator 函數(shù)的改進(jìn)游两,體現(xiàn)在以下四點(diǎn)。
(1)內(nèi)置執(zhí)行器漩绵。
(2)更好的語(yǔ)義贱案。
(3)更廣的適用性。
(4)返回值是 Promise止吐。
異步遍歷的接口
異步遍歷器的最大的語(yǔ)法特點(diǎn)宝踪,就是調(diào)用遍歷器的next方法,返回的是一個(gè) Promise 對(duì)象碍扔。
const asyncIterable = createAsyncIterable(['a', 'b']);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
asyncIterator
.next()
.then(iterResult1 => {
console.log(iterResult1); // { value: 'a', done: false }
return asyncIterator.next();
})
.then(iterResult2 => {
console.log(iterResult2); // { value: 'b', done: false }
return asyncIterator.next();
})
.then(iterResult3 => {
console.log(iterResult3); // { value: undefined, done: true }
});
for await…of
for…of循環(huán)用于遍歷同步的 Iterator 接口瘩燥。新引入的for await…of循環(huán),則是用于遍歷異步的 Iterator 接口不同。
async function f() {
//createAsyncIterable返回一個(gè)異步遍歷器
for await (const x of createAsyncIterable(['a', 'b'])) {
console.log(x);
}
}
// a
// b
Class
基本語(yǔ)法
let methodName = "getArea";
//定義類
class Point {
//constructor方法默認(rèn)返回實(shí)例對(duì)象(即this)厉膀,完全可以指定返回另外一個(gè)對(duì)象。
constructor(x, y) {
this.x = x;
this.y = y;
}
//定義在類的prototype上
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
//可以使用表達(dá)式
[methodName]() {
// ...
}
//定義在構(gòu)造器上的方法
static myMethod(msg) {
console.log('static', msg);
}
//在實(shí)例屬性前加上static關(guān)鍵字
static myStaticProp = 42;
//getter二拐、setter方法
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
class定義的類不存在變量提升
類和模塊的內(nèi)部服鹅,默認(rèn)就是嚴(yán)格模式
new.target返回new命令作用于的那個(gè)構(gòu)造函數(shù)。如果構(gòu)造函數(shù)不是通過(guò)new命令調(diào)用的百新,new.target會(huì)返回undefined
Class的繼承
子類必須在constructor方法中調(diào)用super方法企软,否則新建實(shí)例時(shí)會(huì)報(bào)錯(cuò)。這是因?yàn)樽宇悰](méi)有自己的this對(duì)象吟孙,而是繼承父類的this對(duì)象澜倦,然后對(duì)其進(jìn)行加工。
super雖然代表了父類的構(gòu)造函數(shù)杰妓,但是返回的是子類的實(shí)例藻治,即super內(nèi)部的this指的是ColorPoint,因此super()在這里相當(dāng)于Point.prototype.constructor.call(this)
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 調(diào)用父類的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 調(diào)用父類的toString()
}
}
prototype和__proto__
對(duì)象具有__proto__
巷挥,函數(shù)同時(shí)擁有prototype和__proto__
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
修飾器
修飾器(Decorator)是一個(gè)函數(shù)桩卵,用來(lái)修改類的行為。這是ES7的一個(gè)提案倍宾,目前Babel轉(zhuǎn)碼器已經(jīng)支持雏节。
修飾器本質(zhì)就是編譯時(shí)執(zhí)行的函數(shù)。
function testable(isTestable) {
//target指代目標(biāo)類
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
@testable(false)
class MyClass {}
MyClass.isTestable // false
除了修飾類還能修飾類的成員高职,修飾函數(shù)時(shí)钩乍,參數(shù)為target:對(duì)象原型、name:類的某個(gè)成員名怔锌、descriptor:該成員的描述符寥粹。返回的描述符成為成員的修飾符变过。
由于存在函數(shù)提升,使得修飾器不能用于函數(shù)涝涤。而類不會(huì)提升
Module語(yǔ)法
export
ES6 模塊不是對(duì)象媚狰,而是通過(guò)export命令顯式指定輸出的代碼,再通過(guò)import命令輸入阔拳。這種加載稱為“編譯時(shí)加載”或者靜態(tài)加載崭孤,即 ES6 可以在編譯時(shí)就完成模塊加載。
ES6 的模塊自動(dòng)采用嚴(yán)格模式糊肠,不管你有沒(méi)有在模塊頭部加上”use strict”辨宠。
ES6 模塊之中,頂層的this指向undefined罪针,即不應(yīng)該在頂層代碼使用this彭羹。
export
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
//也可以寫為
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
export {
firstName as fN,
lastName as lN,
year};
export default function () {
console.log('foo');
}
// 錯(cuò)誤
export default var a = 1;
// 正確
export default 42;
export語(yǔ)句輸出的接口黄伊,與其對(duì)應(yīng)的值是動(dòng)態(tài)綁定關(guān)系泪酱,即通過(guò)該接口,可以取到模塊內(nèi)部實(shí)時(shí)的值还最。
import
//導(dǎo)入接口
import {firstName, lastName, year} from './profile';
//設(shè)置別名
import { lastName as surname } from './profile';
//需要有配置文件墓阀,告訴JS引擎該模塊的位置
import {myMethod} from 'util';
//不導(dǎo)入任何值,只執(zhí)行l(wèi)odash模塊代碼
import 'lodash';
import命令具有提升效果拓轻,會(huì)提升到整個(gè)模塊的頭部斯撮,首先執(zhí)行。
有一個(gè)提案扶叉,建議引入import()函數(shù)勿锅,完成動(dòng)態(tài)加載。
const main = document.querySelector('main');
import(`./section-modules/${someVariable}.js`)
.then(module => {
module.loadPageInto(main);
})
export與import混合寫法
export { foo, bar } from 'my_module';
// 等同于
import { foo, bar } from 'my_module';
export { foo, bar };
Module加載實(shí)現(xiàn)
瀏覽器加載 ES6 模塊枣氧,也使用<script>
標(biāo)簽溢十,但是要加入type=”module”屬性。
同一個(gè)模塊如果加載多次达吞,將只執(zhí)行一次张弛。
CommonJS 加載機(jī)制
CommonJS 模塊輸出的是一個(gè)值的拷貝,ES6 模塊輸出的是值的引用酪劫。
CommonJS 模塊是運(yùn)行時(shí)加載吞鸭,ES6 模塊是編譯時(shí)輸出接口。
CommonJS加載模塊是阻塞式加載會(huì)暫停執(zhí)行直到獲取模塊的值,循環(huán)引用時(shí)a調(diào)用b模塊處在阻塞狀態(tài)覆糟,b調(diào)用a模塊只能返回其阻塞前的export值
由于 ES6 輸入的模塊變量刻剥,只是一個(gè)“符號(hào)連接”,所以這個(gè)變量是只讀的滩字,對(duì)它進(jìn)行重新賦值會(huì)報(bào)錯(cuò)造虏。
CommonJS 模塊的輸出緩存機(jī)制盯滚,在 ES6 加載方式下依然有效。
// foo.js
module.exports = 123;
//導(dǎo)入的module.exports將一直是123酗电,而不會(huì)變成null魄藕。
setTimeout(_ => module.exports = null)
ES6模塊轉(zhuǎn)碼
目前瀏覽器并不直接支持模塊,可以使用ES6 module transpiler轉(zhuǎn)為ES5代碼撵术,或者使用SystemJS調(diào)用模塊
ES6 module transpiler
# 首先背率,安裝這個(gè)轉(zhuǎn)碼器。
$ npm install -g es6-module-transpiler
# 然后嫩与,使用compile-modules convert命令寝姿,將 ES6 模塊文件轉(zhuǎn)碼。
$ compile-modules convert file1.js file2.js
# -o參數(shù)可以指定轉(zhuǎn)碼后的文件名划滋。
$ compile-modules convert -o out.js file1.js
SystemJS
<script src="system.js"></script>
<script>
System.import('app/es6-file').then(function(m) {
console.log(new m.q().es6); // hello
});
</script>
參考文獻(xiàn):
ECMAScript 6 入門