ES6特性歸納

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):

  1. 對(duì)象的狀態(tài)不受外界影響锐朴。Promise對(duì)象代表一個(gè)異步操作,有三種狀態(tài):Pending(進(jìn)行中)蔼囊、Resolved(已完成焚志,又稱 Fulfilled)和Rejected(已失敗)畏鼓。只有異步操作的結(jié)果酱酬,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無(wú)法改變這個(gè)狀態(tài)
  2. 一旦狀態(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 入門

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末饵筑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子处坪,更是在濱河造成了極大的恐慌根资,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件同窘,死亡現(xiàn)場(chǎng)離奇詭異玄帕,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)想邦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門裤纹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人丧没,你說(shuō)我怎么就攤上這事鹰椒。” “怎么了呕童?”我有些...
    開(kāi)封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵漆际,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我拉庵,道長(zhǎng)灿椅,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任钞支,我火速辦了婚禮茫蛹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘烁挟。我一直安慰自己婴洼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布撼嗓。 她就那樣靜靜地躺著柬采,像睡著了一般欢唾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上粉捻,一...
    開(kāi)封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天礁遣,我揣著相機(jī)與錄音,去河邊找鬼肩刃。 笑死祟霍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盈包。 我是一名探鬼主播沸呐,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼呢燥!你這毒婦竟也來(lái)了崭添?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤叛氨,失蹤者是張志新(化名)和其女友劉穎呼渣,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體力试,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡徙邻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了畸裳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡淳地,死狀恐怖怖糊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情颇象,我是刑警寧澤伍伤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站遣钳,受9級(jí)特大地震影響扰魂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蕴茴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一劝评、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧倦淀,春花似錦蒋畜、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)插龄。三九已至,卻和暖如春科展,著一層夾襖步出監(jiān)牢的瞬間均牢,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工才睹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留膨处,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓砂竖,卻偏偏與公主長(zhǎng)得像真椿,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乎澄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 以下內(nèi)容是我在學(xué)習(xí)和研究ES6時(shí)突硝,對(duì)ES6的特性、重點(diǎn)和注意事項(xiàng)的提取置济、精練和總結(jié)解恰,可以做為ES6特性的字典;在本...
    科研者閱讀 3,128評(píng)論 2 9
  • 異步編程對(duì)JavaScript語(yǔ)言太重要浙于。Javascript語(yǔ)言的執(zhí)行環(huán)境是“單線程”的护盈,如果沒(méi)有異步編程,根本...
    呼呼哥閱讀 7,311評(píng)論 5 22
  • 本文為阮一峰大神的《ECMAScript 6 入門》的個(gè)人版提純羞酗! babel babel負(fù)責(zé)將JS高級(jí)語(yǔ)法轉(zhuǎn)義腐宋,...
    Devildi已被占用閱讀 1,985評(píng)論 0 4
  • 一、let 和 constlet:變量聲明, const:只讀常量聲明(聲明的時(shí)候賦值)檀轨。 let 與 var 的...
    dadage456閱讀 762評(píng)論 0 0
  • 下面不屬于ECMAScript規(guī)范的范圍的是:( )A胸竞、 數(shù)據(jù)類型B、 語(yǔ)法C参萄、 DOM事件D卫枝、 內(nèi)置對(duì)象和函數(shù)的...
    亓凡閱讀 2,975評(píng)論 3 9