【翻譯】Iterables and iterators in ECMAScript 6

本文翻譯自Dr. Axel Rauschmayer的博客:http://www.2ality.com/2015/02/es6-iteration.html javascript

本文是ES6中iteration的兩篇博客: html

  1. Iterables and iterators in ECMAScript 6 java

  2. ES6 generators in depth node


ECMAScript 6對迭代/遍歷(iteration)引入了一個新的接口: Iterable. 本文解釋了它是如何工做,哪些語句經過它消費數據(例如for-of循環),哪些語句經過它提供數據源(例如arrays)。 python

Iterability

iterability的思想以下: git

  • 數據消費者(Data consumers): JavaScript有一些語句消費數據。例如for-of 循環遍歷 values,spread operator (...)把values插入arrays或function calls。 es6

  • 數據源(Data sources): 數據消費者能夠從許多源頭獲得values。例如想要對一個array的元素遍歷,對一個map中key-value entries的遍歷,或者對一個string中characters的遍歷。 github

讓每一個數據消費者支持全部的數據源並不現實,尤爲是可能會不斷建立新的數據源和消費者,例如經過數據結構或採用新的處理數據方式的庫來建立。所以ES6引入了接口Iterable,數據消費者使用它,數據源實現它: 編程

因爲JavaScript沒有接口概念,Iterable 更多的是個約定: api

  • Source: 一個value被視爲iterable ,當它有一個key爲Symbol.iterator 的方法返回一個iterator。這個iterator是一個對象,經過其方法 next() 返回值,每調用一次next( )返回一個item,那咱們就說它枚舉items

  • Consumption: 數據消費者使用iterator來獲取消費的values。

咱們來看如何消費一個array arr。首先經過key爲Symbol.iterator的方法建立一個iterator: 

    > let arr = ['a', 'b', 'c'];
    > let iter = arr[Symbol.iterator]();

而後重複調用iterator的方法next()獲得array內部的items:

    > iter.next()    { value: 'a', done: false }
    > iter.next()    { value: 'b', done: false }
    > iter.next()    { value: 'c', done: false }
    > iter.next()    { value: undefined, done: true }

next() 返回的每一項被封裝在一個對象中, 該對象的屬性value 對應 item 的value,布爾值屬性done 表示是否到items序列末尾。

Iterable與iterators是遍歷(iteration)的協議(使用遍歷的方法和規則)。該協議的一個關鍵特色是它是序列化的:iterator一次返回一個值。這意味着若是一個iterable數據結構不是線性的(例如是一個樹), 那麼遍歷將使之線性化。

Iterable數據源

下面使用for-of 循環(後面還會詳細解釋)來遍歷各類iterable數據。

Arrays

Arrays (以及typed arrays)能夠對其元素來遍歷:

    for (let x of ['a', 'b']) {
        console.log(x);
    }    
    // Output:
    // 'a'
    // 'b'

Strings

Strings是可遍歷的,但它們枚舉的是Unicode編碼點,每一個Unicode編碼點由1個或2個JavaScript 「characters」組成:

    for (let x of 'a\uD83D\uDC0A') {
        console.log(x);
    }    
    // Output:
    // 'a'
    // '\uD83D\uDC0A' (crocodile emoji)

注意你已經看到了primitive values也是可遍歷的。一個值沒必要是個對象才能遍歷

Maps

Maps [3] 能夠對其entries遍歷。每一個entry被編碼爲一個[key, value] pair,由兩個元素組成的一個數組。這些entries老是能夠被一一枚舉,與它們被插入到map中的順序相同。

    let map = new Map().set('a', 1).set('b', 2);    
    for (let pair of map) {
        console.log(pair);
    }    
    // Output:
    // ['a', 1]
    // ['b', 2]

注意WeakMaps [3]不能夠遍歷。

Sets

Sets [3] 能夠對其entries遍歷。它們被枚舉的順序與插入到set中的順序相同。

    let set = new Set().add('a').add('b');    
    for (let x of set) {
        console.log(x);
    }    
    // Output:
    // 'a'
    // 'b'

注意WeakSets [3] 不能夠遍歷。

arguments

雖然特殊變量arguments 在ECMAScript 6中要被廢棄 (因爲rest parameters), 但它是可遍歷的:

    function printArgs() {
        for (let x of arguments) {
            console.log(x);
        }
    }
    printArgs('a', 'b');    
    // Output:
    // 'a'
    // 'b'

DOM數據結構

大多數DOM數據結構是可遍歷的:

    for (let node of document.querySelectorAll('···')) {
        ···
    }

注意這個功能的實現還在開發中。但實現相對容易,由於symbol Symbol.iterator 不能與現有的property keys [2]相抵觸。

遍歷計算出的數據

並不是全部iterable的內容都來自於數據結構,也能夠是計算中得出的。例如全部的major ES6數據結構(arrays, typed arrays, maps, sets)都有三個方法返回iterable對象:

  • entries() 返回一個可遍歷的entries,編碼爲[key,value] arrays. 對於arrays, values是數組元素,keys是索引。對於sets, 每一個key和value相等,都等於set元素。

  • keys() 返回一個可遍歷的 entries keys。

  • values() 返回一個可遍歷的 entries values。

下面看看. entries() 如何給出array元素及其索引:

    let arr = ['a', 'b', 'c'];    
    for (let pair of arr.entries()) {
        console.log(pair);
    }    
    // Output:
    // [0, 'a']
    // [1, 'b']
    // [2, 'c']

Plain objects不能夠遍歷

Plain objects (由object literals建立)不能夠遍歷:

    for (let x of {}) { // TypeError
        console.log(x);
    }

理由以下。下面兩個活動不一樣:

  1. 檢查一個程序的結構(reflection)

  2. 遍歷數據

最好保持這兩個活動分開。#1與全部的對象相關,#2僅與數據結構相關。能夠向對象Object.prototype添加一個方法[Symbol.iterator]()來使對象可遍歷,但在兩種狀況下會無效

  • 若是是經過 Object.create(null) 建立,那麼 Object.prototype 不在對象的原型鏈中。

  • 若是它們是數據結構,那麼就須要遍歷數據。不只不能對properties遍歷,並且不能添加iterability到已有的類中,由於這將破壞對實例屬性遍歷的代碼。

所以,使properties可遍歷最安全的方式就是經過一個工具函數。例如經過objectEntries(), 其實現見後面 (將來的ECMAScript版本可能會內置相似的實現):

    let obj = { first: 'Jane', last: 'Doe' };    
    for (let [key,value] of objectEntries(obj)) {
        console.log(`${key}: ${value}`);
    }    
    // Output:
    // first: Jane
    // last: Doe

並且,重要的是記住針對對象properties的遍歷主要當對象是maps [4]纔有意義。但這僅在ES5中才這樣作,由於別無他法。在ECMAScript 6中有Map.

Iterating language constructs

本章節列出ES6中內置的全部使用了iteration協議的編程結構。

經過array來分解結構模式

經過array來分解結構(Destructuring [5])模式可針對任意iterable:

    let set = new Set().add('a').add('b').add('c');    
    let [x,y] = set;        
    // x='a'; y='b'
    
    let [first, ...rest] = set;        
    // first='a'; rest=['b','c'];

for-of 循環

for-of 是ECMAScript 6中的新引入的一個循環. 它的一種用法是:

    for (let x of iterable) {
        ···
    }

這個循環遍歷iterable, 將每一個枚舉項賦值給遍歷變量 x ,而後在循環體內處理。x的範圍是循環內,出了循環再也不存在

注意iterable 須要可以遍歷,不然for-of 不能作循環。這就意味着不可遍歷的值必須轉換爲其它可遍歷的。例如經過Array.from(), 能夠將相似於array的值和iterables變爲arrays:

    let arrayLike = { length: 2, 0: 'a', 1: 'b' };    
    
    for (let x of arrayLike) { // TypeError
        console.log(x);
    } 
       
    for (let x of Array.from(arrayLike)) { // OK
        console.log(x);
    }

我期待for-of最好可以替換Array.prototype.forEach()由於它更爲通用,forEach() 只能用於相似於array的值,並且對於很長的項for-of將更快(參見末尾的FAQ)。

遍歷變量:let 聲明 vs. var 聲明

若是用let來聲明遍歷的變量,那麼對每一個遍歷將建立一個新的綁定(slot)。從下面的代碼片斷能夠看出,經過一個箭頭函數將當前的綁定elem保存起來以便後續使用。以後,會看到箭頭函數沒有共享同一個綁定elem, 每一個有不一樣的elem。

    let arr = [];    
    for (let elem of [0, 1, 2]) {
        arr.push(() => elem); // save `elem` for later
    }
    console.log(arr.map(f => f())); // [0, 1, 2]
    
    // `elem`僅存在於循環內部:
    console.log(elem); // ReferenceError: elem is not defined

若是循環中使用var來聲明遍歷的變量看看發生了什麼。如今全部的箭頭函數指向同一個綁定elem。

    let arr = [];    
    for (var elem of [0, 1, 2]) {
        arr.push(() => elem);
    }
    console.log(arr.map(f => f())); // [2, 2, 2]
    
    // `elem` exists in the surrounding function:
    console.log(elem); // 2

當經過循環建立函數(例如添加event listeners)時,每次遍歷有一個綁定就很是有幫助。

在for循環和for-in循環中用let聲明的變量來遍歷

對於for循環和for-in循環,若是用let來聲明遍歷的變量,那麼每次遍歷都會獲得一個綁定。

先看看for循環中用let來聲明遍歷的遍歷i:

    let arr = [];    
    for (let i=0; i<3; i++) {
        arr.push(() => i);
    }
    console.log(arr.map(f => f())); // [0, 1, 2]
    console.log(i); // ReferenceError: i is not defined

若是這裏使用var來聲明i,就會獲得傳統的行爲:

    let arr = [];    
    for (var i=0; i<3; i++) {
        arr.push(() => i);
    }
    console.log(arr.map(f => f())); // [3, 3, 3]
    console.log(i); // 3


相似地,對於for-in循環,用let來聲明遍歷的遍歷key會使得每次遍歷獲得一個綁定:

    let arr = [];    
    for (let key in ['a', 'b', 'c']) {
        arr.push(() => key);
    }
    console.log(arr.map(f => f())); // ['0', '1', '2']
    console.log(key); // ReferenceError: key is not defined

用var來聲明key只會獲得一個綁定:

    let arr = [];    
    for (var key in ['a', 'b', 'c']) {
        arr.push(() => key);
    }
    console.log(arr.map(f => f())); // ['2', '2', '2']
    console.log(key); // '2'

用先定義的變量、對象屬性與數組元素來遍歷

以上只看了for-of中使用一個聲明瞭的變量來遍歷,但還有其餘幾種形式。

能夠用一個先定義的變量來遍歷:

    let x;    
    for (x of ['a', 'b']) {
        console.log(x);
    }

也能夠用一個對象屬性來遍歷:

    let obj = {};    
    for (obj.prop of ['a', 'b']) {
        console.log(obj.prop);
    }

還能夠用一個array元素來遍歷:

    let arr = [];    
    for (arr[0] of ['a', 'b']) {
        console.log(arr[0]);
    }

用解構模式來遍歷

for-of循環與解構組合在一塊兒,用於遍歷key-value對(編碼爲arrays)很是有用。 下面以maps爲例:

    let map = new Map().set(false, 'no').set(true, 'yes');    
    for (let [k,v] of map) {
        console.log(`key = ${k}, value = ${v}`);
    }    
    // Output:
    // key = false, value = no
    // key = true, value = yes

Array.prototype.entries() 也返回一個可遍歷key-value對iterable:

    let arr = ['a', 'b', 'c'];    
    for (let [k,v] of arr.entries()) {
        console.log(`key = ${k}, value = ${v}`);
    }    
    // Output:
    // key = 0, value = a
    // key = 1, value = b
    // key = 2, value = c

所以entries() 能夠根據枚舉項的位置不一樣來作不一樣處理:

    /** Same as arr.join(', ') */
    function toString(arr) {
        let result = '';        
        for (let [i,elem] of arr.entries()) {            
            if (i > 0) {
                result += ', ';
            }
            result += String(elem);
        }        
        return result;
    }

這個函數的調用以下:

    > toString(['eeny', 'meeny', 'miny', 'moe'])    'eeny, meeny, miny, moe'

Array.from()

Array.from() [6] 將iterable和相似array的值轉換爲arrays.,也能夠用於typed arrays.

    > Array.from(new Map().set(false, 'no').set(true, 'yes'))    
    [[false,'no'], [true,'yes']]
    
    > Array.from({ length: 2, 0: 'hello', 1: 'world' })    
    ['hello', 'world']

Array.from() 就像Array的子類同樣 (繼承類的方法) – 將iterables轉換爲子類實例。

Spread

spread操做符[5] 將一個iterable值插入到一個array中:

    > let arr = ['b', 'c'];
    > ['a', ...arr, 'd']
    ['a', 'b', 'c', 'd']

這就提供了一種將任意iterable轉換爲array的緊湊方法:

    let arr = [...iterable];

spread操做符還能夠將一個iterable變爲函數/方法或構造函數的參數:

    > Math.max(...[-1, 8, 3])    
    8

Maps與sets

map的構造函數將一個個由[鍵, 值]對組成的iterable變爲map:

    > let map = new Map([['uno', 'one'], ['dos', 'two']]);
    > map.get('uno')    
    'one'
    > map.get('dos')    
    'two'

set的構造函數將一個個由元素組成的iterable變爲set:

    > let set = new Set(['red', 'green', 'blue']);
    > set.has('red')    
    true
    > set.has('yellow')    
    false

WeakMap與WeakSet 的構造函數與上述相似。並且maps與sets自己就是iterable(WeakMaps與WeakSets不是),這意味着能夠用它們的構造函數來克隆它們。

Promises

Promise.all()和Promise.race()接受iterables over promises [7]:

    Promise.all(iterableOverPromises).then(···);
    Promise.race(iterableOverPromises).then(···);

yield*

yield* [8] 會對一個iterable全部的枚舉項讓步.

    function* yieldAllValuesOf(iterable) {
        yield* iterable;
    }

yield*最重要的一個用法是遞歸調用一個generator [8] (這將產生某種iterable).

實現iterables

遍歷協議相似於下面這樣:

若是一個對象(擁有或繼承)有key爲Symbol.iterator的方法,那麼該對象就是可遍歷對象iterable (即實現Iterable接口) ,這個方法必須返回一個iterator, 經過其方法next()來枚舉可遍歷對象內部的每一項。

在TypeScript中,iterables和iterators的接口相似於下面這樣(參見 [9]):

    interface Iterable {
        [System.iterator]() : Iterator;
    }
    interface IteratorResult {
        value: any;
        done: boolean;
    }
    interface Iterator {
        next(value? : any) : IteratorResult;        
        return?() : IteratorResult;
    }

return 是一個可選方法,在後面介紹 (throw()也是可選方法,但實際上也從不用於iterators,將在下一篇博客中討論a follow-up blog post on generators). 首先實現一個dummy iterable來感覺下iteration是如何工做的。

    let iterable = {
        [Symbol.iterator]() {            
            let step = 0;            
            let iterator = {
                next() {                    
                    if (step <= 2) {
                        step++;
                    }                    
                    switch (step) {                        
                        case 1:                            
                            return { value: 'hello', done: false }; 
                        case 2:                            
                            return { value: 'world', done: false }; 
                        default:                            
                            return { value: undefined, done: true };
                    }
                }
            };            
            return iterator;
        }
    };

如今來檢查iterable:

    for (let x of iterable) {
        console.log(x);
    }    
    // Output:
    // hello
    // world

上面代碼執行三步,用計數器step 來確保每一步輸出對應的值。首先返回'hello',接下來返回'world',而後枚舉項遍歷結束。每一項都被封裝在有下面屬性的對象中:

  • value 對應實際的值

  • done 是個布爾標誌, 表示是否遍歷結束。

若是done 取值爲 false或者value 是undefined能夠忽略。重寫上面的 switch 語句:

    switch (step) {        
        case 1:            
            return { value: 'hello' };        
        case 2:            
            return { value: 'world' };        
        default:            
            return { done: true };
    }

在下一篇博客中會解釋 the follow-up blog post on generators, 有些狀況下但願donetrue 時最後一項能夠返回一個value. 不然的話,能夠更簡化next() 的實現,直接返回items而不用封裝在對象中。可經過一個特殊值(例如一個符號symbol)來表示遍歷結束。

下面再看一個iterable的實現,函數iterateOver() 返回一個對傳入參數的遍歷:

    function iterateOver(...args) {
        let index = 0;        
        let iterable = {
            [Symbol.iterator]() {                
                let iterator = {
                    next() {                        
                        if (index < args.length) {                            
                            return { value: args[index++] };
                        } else {                            
                            return { done: true };
                        }
                    }
                };                
                return iterator;
            }
        }        
        return iterable;
    }    
    // Using `iterateOver()`:
    for (let x of iterateOver('fee', 'fi', 'fo', 'fum')) {
        console.log(x);
    }    
    // Output:
    // fee
    // fi
    // fo
    // fum

Iterators是可遍歷的(iterable)

若是iterable和iterator是同一個對象,那麼前面的函數能夠簡化爲:

    function iterateOver(...args) {
        let index = 0;        
        let iterable = {
            [Symbol.iterator]() {                
                return this;
            },
            next() {                
                if (index < args.length) {                    
                    return { value: args[index++] };
                } else {                    
                    return { done: true };
                }
            },
        };        
        return iterable;
    }

即便最初的iterable和iterator不是同一個對象,但若是iterator有下面方法(這也使得iterator是可遍歷的),那麼有時候也有用處:

    [Symbol.iterator]() {        
        return this;
    }

ES6中全部內置的iterators都遵循這個模式(經過一個公共prototype, 參見follow-up blog post on generators). 例如,arrays的缺省iterator:

    > let arr = [];
    > let iterator = arr[Symbol.iterator]();
    > iterator[Symbol.iterator]() === iterator    true

爲何當一個iterator是可遍歷的(便是一個iterable)有用呢? for-of 僅用於iterables, 而不能用於iterators. 正是因爲array iterators是iterable, 所以可在另外一個循環中繼續遍歷:

    let arr = ['a', 'b'];    
    let iterator = arr[Symbol.iterator]();    
    for (let x of iterator) {
        console.log(x); // a
        break;
    }    
    // Continue with same iterator:
    for (let x of iterator) {
        console.log(x); // b
    }

另外一種方式就是用方法返回一個iterable。例如Array.prototype.values() 返回結果與缺省遍歷方式相同。所以前面的代碼段等同於下面代碼:

    let arr = ['a', 'b'];    
    let iterable = arr.values();    
    for (let x of iterable) {
        console.log(x); // a
        break;
    }    
    for (let x of iterable) {
        console.log(x); // b
    }

但使用iterable時,若是 for-of 調用了方法[Symbol.iterator]()不能確保不會從新開始遍歷。例如Array 的實例是iterables,當調用這個方法時將從頭開始遍歷。

繼續遍歷的一個用況是在經過 for-of 處理實際內容前先去掉初始項(例如一個header)。

可選的iterator方法: return() 與 throw()

兩個iterator方法是可選的:

  • return() 當遍歷過早終止時讓iterator有機會清理殘局。

  • throw() 轉發方法調用給一個經過yield*來遍歷的generator,詳細解釋見the follow-up blog post on generators.

經過return()關閉iterators

前面提到,可選的iterator方法return() 就是尚未遍歷結束時就讓iterator中止,即關閉iterator。在for-of循環中,會因爲如下緣由過早終止premature(在語言規範中稱中斷abrupt) :

  • break

  • continue (若是在外部循環中繼續遍歷, continue行爲相似於break)

  • throw

  • return

在上面四種狀況下,for-of讓iterator知道循環沒有結束。下面看一個例子,函數readLinesSync 返回對一個文件每一文本行的遍歷,不論發生什麼都會關閉文件:

    function readLinesSync(fileName) {
        let file = ···;        
        return {
            ···
            next() {                
                if (file.isAtEndOfFile()) {
                    file.close();                    
                    return { done: true };
                }
                ···
            },            
            return() {
                file.close();                
                return { done: true };
            },
        };
    }

因爲return(), 在下面循環中文件將被關閉:

    // Only print first line
    for (let line of readLinesSync(fileName)) {
        console.log(x);        
        break;
    }

return() 方法必須返回一個對象,這是因爲generators處理return語句的方式,具體將在the follow-up blog post on generators解釋。

下面的constructs會關閉尚未遍歷到結尾的iterators:

  • for-of

  • yield*

  • Destructuring

  • Array.from()

  • Map(), Set(), WeakMap(), WeakSet()

  • Promise.all(), Promise.race()

iterables更多例子

在這一章節中來看更多iterables例子。這些iterables大多數經過generators更容易實現,詳見The follow-up blog post on generators

返回iterables的工具函數

返回iterables的工具函數和方法就像iterable數據結構同樣重要。下面是一個遍歷對象屬性的工具函數:

    function objectEntries(obj) {
        let index = 0;    
        // In ES6, you can use strings or symbols as property keys,
        // Reflect.ownKeys() retrieves both
        let propKeys = Reflect.ownKeys(obj);    
        return {
            [Symbol.iterator]() {                
                return this;
            },
            next() {                
                if (index < propKeys.length) {                    
                    let key = propKeys[index];
                    index++;                    
                    return { value: [key, obj[key]] };
                } else {                    
                    return { done: true };
                }
            }
        };
    }    
    let obj = { first: 'Jane', last: 'Doe' };    
    for (let [key,value] of objectEntries(obj)) {
        console.log(`${key}: ${value}`);
    }    
    // Output:
    // first: Jane
    // last: Doe

iterables組合函數

Combinators [10] 是組合已有iterables並建立新的iterable的函數。

先來看一個combinator函數take(n, iterable), 它返回一個iterable,由iterable前面 n 項組成。

    function take(n, iterable) {
        let iter = iterable[Symbol.iterator]();        
        return {
            [Symbol.iterator]() {                
                return this;
            },
            next() {                
                if (n > 0) {
                    n--;                    
                    return iter.next();
                } else {                    
                    return { done: true };
                }
            }
        };
    }    
    let arr = ['a', 'b', 'c', 'd'];    
    for (let x of take(2, arr)) {
        console.log(x);
    }    
    // Output:
    // a
    // b

zip 將n 個iterables組合爲一個由n元數組(每一個元數組tuple的編碼是一個長度爲n的數組)組成的iterable。 

    function zip(...iterables) {
        let iterators = iterables.map(i => i[Symbol.iterator]());        
        let done = false;        
        return {
            [Symbol.iterator]() {                
                return this;
            },
            next() {                
                if (!done) {                    
                    let items = iterators.map(i => i.next());
                    done = items.some(item => item.done);                    
                    if (!done) {                        
                        return { value: items.map(i => i.value) };
                    }                    
                    // Done for the first time: close all iterators
                    for (let iterator of iterators) {
                        iterator.return();
                    }
                }                
                // We are done
                return { done: true };
            }
        }
    }

能夠看出,最短的iterable決定最終長度:

    let zipped = zip(['a', 'b', 'c'], ['d', 'e', 'f', 'g']);    
    for (let x of zipped) {
        console.log(x);
    }    
    // Output:
    // ['a', 'd']
    // ['b', 'e']
    // ['c', 'f']

無窮盡iterables

有些iterable可能永遠不會結束done: 

    function naturalNumbers() {
        let n = 0;        
        return {
            [Symbol.iterator]() {                
                return this;
            },
            next() {                
                return { value: n++ };
            }
        }
    }

對無窮盡iterable,必定不能遍歷全部元素。例如可從一個for-of循環跳出:

    for (let x of naturalNumbers()) {        
        if (x > 2) break;
        console.log(x);
    }

或者僅訪問無窮盡iterable的前面幾項:

    let [a, b, c] = naturalNumbers();        
    // a=0; b=1; c=2;

或者使用一個combinator函數。例如可使用take()函數:

    for (let x of take(3, naturalNumbers())) {
        console.log(x);
    }    // Output:
    // 0
    // 1
    // 2

由zip() 返回的iterable的長度是由最短的iterable決定,即zip()和naturalNumbers() 就能夠返回任意長度的iterable,包括無窮盡長度:

    let zipped = zip(['a', 'b', 'c'], naturalNumbers());    
    for (let x of zipped) {
        console.log(x);
    }    
    // Output:
    // ['a', 0]
    // ['b', 1]
    // ['c', 2]

常見問題(FAQ)

iteration協議慢嗎?

你可能擔憂遍歷協議很慢,由於每次調用next()須要建立一個新對象。但內存管理小對象對於現代引擎以及長時間運行是很快的,引擎能夠優化遍歷不須要爲中間對象分配空間。更多信息可參見thread on es-discuss

結論

這篇博文雖然只涉及了ES6 iteration的基礎,但內容已經不少了。Generators [8]以iterators的實現爲基礎。

JavaScript運行時庫尚未與iterators工做的工具,Python有特性豐富的模塊itertools, JavaScript最終也會有相似的模塊。

進一步閱讀

  1. Exploring ES6: Upgrade to the next version of JavaScript」, book by Axel

  2. Symbols in ECMAScript 6

  3. ECMAScript 6: maps and sets

  4. Pitfalls: Using an Object as a Map」 in 「Speaking JavaScript」

  5. Destructuring and parameter handling in ECMAScript 6 [includes an explanation of the spread operator (...)]

  6. ECMAScript 6’s new array methods

  7. ECMAScript 6 promises (2/2): the API

  8. ES6 generators in depth

  9. Closing iterators」, slides by David Herman

  10. Combinator」 in HaskellWiki

相關文章
相關標籤/搜索