ES6 實用點記錄

原文地址





let


用法類似 var

(() => {
  for (var i = 0; i < 6; i++) {}
  console.log(i);  // 6
})();

(() => {
  for (let i = 0; i < 6; i++) {}
  console.log(i);  // ReferenceError: i is not defined
})();

# 高頻場景

(() => {
    var a = [];
    for (var i = 0; i < 10; i++) {
        a[i] = function () {
            console.log(i);
        }
    }
    a[6]();  // 10
})();


(() => {
    var a = [];
    for (let i = 0; i < 10; i++) {
        a[i] = function () {
            console.log(i);
        }
    }
    a[6]();  // 6
})();

# 沒有 變量提升

(() => {
    var a = [];
    for (var i = 0; i < 10; i++) {
        a[i] = function () {
            console.log(i);
        }
    }
    a[6]();  // 10
})();


(() => {
    var a = [];
    for (let i = 0; i < 10; i++) {
        a[i] = function () {
            console.log(i);    
        }
    }
    a[6]();  // 6
})();

# 暫時性死區

// let 聲明之前使用會報錯
(() => {
    if (true) {
        typeof undefined_save; // 不報錯
        typeof save;           // ReferenceError: save is not defined
        let save;
    }
})();

# 不能重複聲明

(() => {
    let save = 7;
    let save = 6;  // SyntaxError: Identifier 'save' has already been declared

    var you = 7;
    var you = 6;   // OK
})();



解構解析

# 普通解析

(() => {
    /**
     *  數組
     */
    let [a,b,c] = [1, 3, 4];
    console.log(a);  // 1
    console.log(b);  // 3
    console.log(c);  // 4


    /**
     *  數組中數組
     */   
    let [v1, [[v2], v3]] = [1, [[2], 6]];
    console.log(v1);  // 1
    console.log(v2);  // 2
    console.log(v3);  // 6

    /**
     *  解析不到
     */
    let [v4] = [];
    let [v5, v6] = [6];
    console.log(v4);  // undefined
    console.log(v5);  // 6
    console.log(v6);  // undefined
})();
(() => {
    /**
     *  默認值
     */
    let [v1 = true] = [];
    let [v2, v3 = 'b'] = ['a'];
    let [v4, v5 = 'b'] = ['a', undefined];
    console.log(v1);  // true
    console.log(v2);  // a
    console.log(v3);  // b
    console.log(v4);  // a
    console.log(v5);  // b

    /**
     *  null 生效
     *  undefined 不生效
     */
    let [v6 = 'save'] = [undefined];
    let [v7 = 26] = [null];
    console.log(v6);  // save
    console.log(v7);  // null
})();

# 對象解析

普通對象

(() => {
    let object = {
        array: [
            'Save',
            {
                you: 'you from anything'
            }
        ]
    };
    let {array: [save, {you}]} = object;
    console.log(save);  // Save
    console.log(you);   // you from anything
})();    

嵌套對象

(() => {
  (() => {
      let object = {};
      let array = [];
      ({save: object.save, you: array[7]} = {save: 'Save', you: 'you'});
      console.log(object.save);  // Save
      console.log(array[7]);     // you from anything
  })();   

系統類對象

(() => {
    /**
     * Math
     */
    let {log: mathLog, sin: mathSin, cos: mathCos} = Math;
    console.log(mathLog);
    console.log(mathSin);
    console.log(mathCos);

    /**
     * Array
     */
    let array = ["Save", "you", "from", "anything"];
    let {length: count, 0: firstElement, [array.length - 1]: lastElement} = array;
    console.log(count);         // 4
    console.log(firstElement);  // Save
    console.log(lastElement);   // anything

    /**
     * String
     */
    const [s,a,v,e] = "Save";
    console.log(s);             // S
    console.log(a);             // a
    console.log(v);             // v
    console.log(v);             // e
})();



String 擴展

# 模板編譯

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板編譯</title>
</head>
<body>

<div id="div"></div>
<script>

    let template = `
        <ul>
          <% for(var i=0; i < data.supplies.length; i++) { %>
            <li><%= data.supplies[i] %></li>
          <% } %>
        </ul>
        `;

    function compile(template) {
        let evalExpr = /<%=(.+?)%>/g;
        let expr = /<%([\s\S]+?)%>/g;

        template = template
            .replace(evalExpr, '`); \n  echo( $1 ); \n  echo(`')
            .replace(expr, '`); \n $1 \n  echo(`');

        template = 'echo(`' + template + '`);';

        let script =
            `(function parse(data){
                    var output = "";

                    function echo(html){
                      output += html;
                    }

                    ${ template }

                    return output;
        })`;

        return script;
    }
    let div = document.getElementById("div");
    let parse = eval(compile(template));
    div.innerHTML = parse({supplies: ["CaMnter", "Save you from anything", "魔法使"]});
</script>
</body>
</html>



Number 擴展

# Number.isNaN

Number.isNaN(...): 判斷一個值是否爲 NaN

(() => {
    /**
     * ES6
     */
    console.log(Number.isNaN(NaN));  // true

    /**
     * ES5
     */
    (function (global) {
        var global_isNaN = global.isNaN;
        Object.defineProperty(Number, 'isNaN', {
            value: function isNaN(value) {
                return typeof value === 'number' && global_isNaN(value);
            },
            configurable: true,
            enumerable: false,
            writable: true
        });
    })(this);
    console.log(Number.isNaN(NaN));   // true
})();

# Number.isSafeInteger

Number.isSafeInteger(...): 來判斷一個整數是否落在 -2^53 ~ 2^53 之內。

JavaScript 能夠準確表示的整數範圍在 -2^53 ~ 2^53 之間(不含兩個端點),超過這個範圍,無法精確表示這個值。

(() => {
    /**
     * ES6
     */
    console.log(Number.isSafeInteger(9007199254740990));  // true
    console.log(Number.isSafeInteger(9007199254740992));  // false

    /**
     * ES5
     */
    Number.isSafeInteger = function (n) {
        return (typeof n === 'number' &&
        Math.round(n) === n &&
        Number.MIN_SAFE_INTEGER <= n &&
        n <= Number.MAX_SAFE_INTEGER);
    }
    console.log(Number.isSafeInteger(9007199254740990));  // true
    console.log(Number.isSafeInteger(9007199254740992));  // false
})();



Array 擴展

# Array.of

Array.of(...): 方法用於將一組值,轉換爲數組。

(() => {
    // ES6
    console.log(Array.of());           // []
    console.log(Array.of(undefined));  // [ undefined ]
    console.log(Array.of(7));          // [ 7 ]
    console.log(Array.of(6, 7));       // [ 6, 7 ]

    // ES5 部署 Array.Of()
    function ArrayOf() {
        return [].slice.call(arguments);
    }

    console.log(Array.of(7));          // [ 7 ]
})();

# Array.includes

Array.prototype.includes: 方法返回一個布爾值,表示某個數組是否包含給定的值。

(() => {
    // 屬於 ES7 語法
    // ES6 部署 includes
    const arrayIncludes = (() => Array.prototype.includes ?
        (array, value) => array.includes(value) :
        (array, value) => array.some(el => el === value))();
    console.log(arrayIncludes(['Save', 'you', 'from', 'anything'], 'Save'));  // true
})();



Function 擴展

# 參數 默認值

(() => {
    const SAVE = "Save";
    /**
     * ES6
     */
    function es6function(value = SAVE) {
        console.log(value);
    }
    es6function();  // Save
    /**
     * ES5
     */
    function es5function(value) {
        if (typeof value === 'undefined') {
            value = SAVE;
        }
        console.log(value);
    }
    es5function();  // Save
})();

# 參數 解構解析

(() => {
    const SAVE = "Save";

    function func({index, sign = SAVE}) {
        console.log(index, sign);
    }

    func({index: 67, sign: 'you'});  // 67 'you'
    func({index: 67});               // 67 'Save'
    func({});                        // undefined 'Save'
})();

# 參數可省 與 參數不可省

(() => {
    /**
     * 參數可省
     */
    function funcA(v = undefined) {
        return v;
    }

    funcA();

    /**
     * 參數不可省
     */
    function throwIfMissing() {
        throw new Error('Missing parameter');
    }

    function funcB(v = throwIfMissing()) {
        return v;
    }

    funcB();  // Error: Missing parameter
})();

# rest 參數

rest 參數...變量名,代表一個數組 。

(() => {
    function func(array, ...items) {
        items.forEach(function (item) {
            array.push(item);
        });
    }

    let array = [];
    func(array, 2, 6, 7);
    console.log(array);    // [ 2, 6, 7 ]
})();

# 擴展運算符

擴展運算符...數組,解數組。將數組轉爲用逗號分隔的參數序列。

(() => {
    function func(v, w, x, y, z) {
        return v + w + x + y + z;
    }

    let args = [2, 3, 3];
    console.log(func(-1, ...args, 2, ...[6, 7]));  // 9
})();

# 箭頭函數嵌套

(() => {
    let insertFunction = target => ({
        into: array => ({
            after: value => {
                array.splice(array.indexOf(value) + 1, 0, target);
                return array;
            }
        })
    });
    console.log(insertFunction(6).into([2, 7]).after(2));  // [ 2, 6, 7 ]
})();



Object 擴展

# 屬性簡潔表示

(() => {
    let save = "Save";
    let object = {save};                     // 等於 let object = {save: save};
    console.log(object.save);                // Save

    function func(x, y) {
                                             // 等於 return {x: x, y: y};
        return {x, y};
    }

    console.log(func('CaMnter', '2233').x);  // CaMnter
})();

# 方法簡寫

方法簡寫 更像 Java 的方法聲明。

(() => {
    let object = {
        method(value){                   // 等於 method: function (value)
            return value;
        }
    }
    console.log(object.method('Save'));  // Save
})();



Symbol

Symbol 保證每個屬性的名字都是獨一無二,從根本上防止屬性名的衝突。
這就是 ES6 引入 Symbol 的原因。

ES6 引入了一種新的原始數據類型 Symbol
JavaScript 語言的第七種數據類型。
前六種是:UndefinedNullBooleanStringNumber、對象Object

ES6 以後, 對象的屬性名現在可以有兩種類型. 一種是 String, 一種是 Symbol
凡是屬性名屬於 Symbol 類型, 就都是獨一無二的, 可以保證不會與其他屬性名產生衝突。

# 唯一性

(() => {
    let symbol = Symbol('Save');
    let object = {
        toString(){
            return 'object-toString';
        }
    }
    console.log(symbol);                 // Symbol(Save)
    console.log(typeof symbol);          // symbol
    console.log(Symbol(object));         // Symbol(object-toString)
    console.log(Symbol() === Symbol());  // true
})();



Proxy

ES6 原生提供 Proxy 構造函數:
let proxy = new Proxy(target, handler);
target 參數表示所要攔截的目標對象。
handler 參數也是一個對象,用來定製攔截行爲。

注意: 要使得 Proxy 起作用,必須針對 Proxy 實例進行操作,而不是針對 目標對象 進行操作。

Proxy 實際上 重載 了 點運算符
即用自己的定義覆蓋了語言的原始定義。

Proxy 能代理的方法,在 Reflect 都可以找得到。

# Proxy 實現 - 負數索引 的數組

(() => {
    function createSmartArray(...elements) {
        let minusIndexArrayHandler = {
            get(target, propertyKey, receiver) {
                let index = Number(propertyKey);
                return Reflect.get(
                    target,
                    index < 0 ?
                        String(index + target.length) :
                        propertyKey,
                    receiver);
            }
        };
        let target = [];
        target.push(...elements);
        return new Proxy(target, minusIndexArrayHandler);
    }

    let array = createSmartArray(0, 1, 2, 3, 4, 5);
    console.log(array[-1]);  // 5
    console.log(array[-2]);  // 4
})();

# Proxy 實現 - 鏈式操作

(() => {
    let double = n => n * 2;
    let pow = n => n * n;
    let reverseInt = n => n.toString().split("").reverse().join("") | 0;

    let pipeMap = new Map();
    pipeMap.set(double.name, double);
    pipeMap.set(pow.name, pow);
    pipeMap.set(reverseInt.name, reverseInt);

    let pipe = (function () {
        return function (value) {
            let funcStack = [];
            let proxy = new Proxy({}, {
                get: function (target, propertyKey, receiver) {
                    if (propertyKey === 'get') {
                        return funcStack.reduce(function (previousFunction, currentFunction) {
                            return currentFunction(previousFunction);
                        }, value);
                    }
                    funcStack.push(pipeMap.get(propertyKey));
                    return proxy;
                }
            });
            return proxy;
        }
    }());
    console.log(pipe(3).double.pow.reverseInt.get);  // 63
})();

# Proxy 實現 - 設置屬性非 null

(() => {
    let validator = {
        set(target, propertyKey, value, receiver) {
            if (propertyKey === 'save' &&
                value == null) {
                throw new TypeError('The save is not null');
            } else {
                return Reflect.set(target, propertyKey, value, receiver);
            }
        }
    }
    let camnter = new Proxy({}, validator);
    camnter.save = 'save';
    console.log(camnter.save);  // save
    camnter.save = null;        // TypeError: The save is not null
})();

# Proxy 實現 - 屏蔽外部調用 _??? 屬性

(() => {
    let handler = {
        get(target, propertyKey, receiver) {
            this.invariant(propertyKey, 'get');
            return Reflect.get(target, propertyKey, receiver);
        },
        set(target, propertyKey, value, receiver) {
            this.invariant(propertyKey, 'set');
            Reflect.set(target, propertyKey, value, receiver);
        },
        invariant (key, action) {
            if (key[0] === '_') {
                throw new Error(`Invalid attempt to ${action} private "${key}" property`);
            }
        }
    };
    let proxy = new Proxy({
        _save: 'Save you from anything'
    }, handler);
    proxy._save;           // Error: Invalid attempt to get private "_save" property
    proxy._save = '2233';  // Error: Invalid attempt to set private "_save" property
})();

# Proxy 實現 - 執行兩次方法

(() => {
    function sum(left, right) {
        return left + right;
    }

    let proxy = new Proxy(sum, {
        apply(target, thisArgument, argumentsList){
            return Reflect.apply(...arguments) * 2;
        }
    })
    console.log(proxy(22, 33));                         // 110
    console.log(proxy.call(null, 22, 33));              // 110
    console.log(proxy.apply(null, [22, 33]));           // 110

    // 調用 Reflect.apply 方法,也會被攔截
    console.log(Reflect.apply(proxy, null, [22, 33]));  // 110
})();

# Proxy 實現 - 隱藏 _??? 屬性

(() => {
    let target = {_save: 'save', save: 'save'};
    let proxy = new Proxy(target, {
        has(target, propertyKey){
            if (propertyKey[0] === '_')
                return false;
            return Reflect.has(target, propertyKey);
        }
    });
    console.log('_save' in proxy);  // true
})();

# Proxy 實現 - 對象 不可配置 和 禁止擴展

(() => {
    let object = {save: 'save'};
    Object.preventExtensions(object);  // 設置對象不可擴展
    let proxy = new Proxy(object, {
        has(target, propertyKey){
            return false;
        }
    });
    console.log('save' in proxy);      // TypeError: 'has' on proxy: trap returned falsish for property 'save' but the proxy target is not extensible
})();

# Proxy 實現 - 不允許刪除 _??? 屬性

(() => {
    let target = {_save: 'save'};
    let proxy = new Proxy(target, {
        deleteProperty(target, propertyKey){
            this.invariant(propertyKey, 'delete');
            Reflect.deleteProperty(target, propertyKey);
        },
        invariant(propertyKey, action){
            if (propertyKey[0] === '_') {
                throw new Error(`Invalid attempt to ${action} private "${propertyKey}" property`);
            }
        }
    })
    delete proxy._save;  // Error: Invalid attempt to delete private "_save" property
})();

# Proxy 實現 - 過濾掉 _??? 屬性

(() => {
    let target = {
        save: 'save',
        _save: '_save'
    };
    let proxy = new Proxy(target, {
        ownKeys(target){
            return Reflect.ownKeys(target).filter(key => key[0] !== '_');
        }
    });
    console.log(Object.keys(proxy));  // [ 'save' ]
})();

# Proxy.revocable 取消代理

(() => {
    let target = {};
    let {proxy, revoke} = Proxy.revocable(target, {});
    proxy.save = '_save';
    console.log(proxy.save);  // _save
    revoke();
    proxy.save;               // TypeError: Cannot perform 'get' on a proxy that has been revoked
})();

# Proxy 重點 - this 問題

Proxy 可以代理針對目標對象的訪問,但它不是目標對象的透明代理。
在不做任何攔截的情況下,也無法保證與目標對象的行爲一致。

主要原因:
Proxy 代理的情況下,目標對象內部的 this 關鍵字會指向 Proxy 代理。

(() => {
    let target = {
        compareThis: function () {
            return this === proxy;
        }
    };
    let proxy = new Proxy(target, {});
    console.log(target.compareThis());  // false
    console.log(proxy.compareThis());   // true
})();

this 指向的變化,導致 Proxy 無法代理目標對象。

(() => {
    let personMap = new WeakMap();
    class Person {
        constructor(name) {
            personMap.set(this, name);
        }

        get name() {
            let name = personMap.get(this);
            console.log(name);
            return name;
        }
    }
    let you = new Person('you');
    you.name;                           // you

    let youProxy = new Proxy(you, {});
    /*
     * youProxy.name 訪問時
     * this 指向 youProxy
     * 導致無法取到值,所以返回 undefined
     */
    youProxy.name;                      // undefined
})();

有些原生對象的內部屬性,只有通過正確的 this 才能拿到。
Proxy 也無法代理這些原生對象的屬性。
解決辦法: this 綁定原對象。

(() => {
    let date = new Date('2017-02-07');
    let proxy = new Proxy(date, {});
    proxy.getDate();                     // TypeError: this is not a Date object.

    let getDateHandler = {
        get(target, propertyKey, receiver){
            if (propertyKey === 'getDate') {
                return target.getDate.bind(target);
            }
            return Reflect.get(target, propertyKey, receiver);
        }
    }
    let smartProxy = new Proxy(date, getDateHandler);
    console.log(smartProxy.getDate());    // 7
})();



Promise

Promise 是異步編程的一種方案,用於替代原本的 “回調和事件” 模式。
Promise 的概念最早由社區提出來的,後來 ES6 統一用戶,原生提供了 Promise
Promise 是一個對象,可以獲取異步操作的消息。各種異步操作可以用同樣的方法進行處理。


特點:

  • 1.Promise 對象的狀態不受外界影響。Promise 代表一個異步操作,有三種狀態:Pending:進行中Resoled:已完成Rejected:已失敗
  • 2. 一旦狀態改變,就不會再次變化,何時都能得到結果。
    狀態變化只有 兩種可能:
    Pending >> Resoled
    Pending >> Rejected
    一旦變化就一直保持這個結果。


Promise 可以將異步操作以同步操作的形式表達出來,避免了層層嵌套的回調,統一的接口,使得控制異步操作更容易。


Promise 的缺點:

  • 1. 無法取消 Promise,創建就立即執行,無法中途取消。
  • 2. 不設置回調函數的話,Promise 內部拋出錯誤,也不會反應到外部。
  • 3. 處於 Pending 狀態時,是無法知道執行到哪一階段了。


# Promise 簡單介紹

Promise 的構造方法接收一個 方法。
這個方法 有兩個參數,這兩個參數也是 方法,分別是 resolvereject
是由 JavaScript 引擎提供。

resolve 方法的作用:將 Promise 對象的狀態從 Pending >> Resolved
將異步操作成功的結果,作爲參數傳遞
reject 方法的作用:將 Promise 對象的狀態從 Pending >> Reject
將異步操作失敗的錯誤,作爲參數傳遞

(() => {
    let promiseA = new Promise((resolve, reject) => {
        resolve(200);  // 異步成功
    });
    let promiseB = new Promise((resolve, reject) => {
        reject(400);   // 異步失敗
    });
})();

# Promise 實現 - 異步加載圖片

(() => {
    function loadImageAsync(url) {
        return new Promise((resolve, reject) => {
            let image = new Image();
            image.onload = function () {
                resolve(image);
            };
            image.onerror = function () {
                reject(new Error('Could not load image at: ' + url));
            };
            image.src = url;
        });
    };
})();

# Promise 實現 - Ajax + Promise

(() => {
    let getJSON = function (url) {
        return new Promise((resolve, reject) => {
            function handler() {
                if (this.readyState !== 4) {
                    return;
                } else if (this.status == 200) {
                    resolve(this.response);
                } else {
                    reject(new Error(this.statusText));
                }
            };
            let client = new XMLHttpRequest();
            client.open('GET', url);
            client.onreadystatechange = handler;
            client.responseType = 'json';
            client.setRequestHeader('Accept', 'application/json');
            client.send();
        });
    };
    getJSON("https://www.camnter.com")
        .then(json => {
            // success
        }, error => {
            // failure
        });
})();

# Promise 嵌套

(() => {
    let promiseA = new Promise((resolve, reject) => {
        setTimeout(() => reject(new Error('fail')), 2000);
    });
    let promiseB = new Promise((resolve, reject) => {
        setTimeout(() => resolve(promiseA), 1000);  // resolve Promise 後,自身的狀態無效了
    });
    promiseB
        .then(promise => {
            console.log(promise);                   // Error: fail
        })
        .catch(error => {
            console.log(error);
        })
})();

# Promise.then

Promisethen 方法定義在 原型對象 Promise.prototype 上的。
它的作用是:爲 Promise 實例添加狀態改變時的回調方法。

第一個參數是 Resolved 狀態的回調方法。
第二個參數是 Rejected 狀態的回調方法。

/**
 * then 進行數據轉換
 */
(() => {
    new Promise((resolve, reject) => {
        resolve(67);
    }).then(value => {
        return 'Save you from anything ' + value;
    }).then(string => {
        console.log(string);  // Save you from anything 67
    });
})();

如果 then 返回了 Promise,這個 then 後面的 then 都會等待這個 Promise 的狀態發生變化後,纔會調用。

(() => {
    new Promise((resolve, reject) => {
        resolve(true);
    }).then(call => {
        if (!call)return null;
        return new Promise((resolve, reject) => {
            setTimeout(() => reject(new Error('異空間錯誤 404 ')), 677);
        });
    }).then(value => {
        console.log(value);
    }, error => {
        console.log(error);     // Error: 異空間錯誤 404
    });
})();

# Promise.catch

實際上: Promise.prototype.catch == .then(null, rejection)

(() => {
    new Promise((resolve, reject) => {
        reject(new Error('異空間錯誤 404'));
    }).then(value => {
    }).then(null, error => {
        console.log(error);  // Error: 異空間錯誤 404
    })

    // 等同於

    new Promise((resolve, reject) => {
        reject(new Error('異空間錯誤 404'));
    }).then(value => {
    }).catch(error => {
        console.log(error);  // Error: 異空間錯誤 404
    });
})();

/**
 * reject 等同於 拋出錯誤
 */
(() => {
    new Promise((resolve, reject) => {
        reject(new Error('異空間錯誤 404'));
    }).catch(error => {
        console.log(error);  // Error: 異空間錯誤 404
    });

    // 等同於

    new Promise((resolve, reject) => {
        try {
            throw new Error(new Error('異空間錯誤 404'));
        } catch (e) {
            reject(e);
        }
    }).catch(error => {
        console.log(error);  // Error: 異空間錯誤 404
    });
})();

# Promise 錯誤 “冒泡” 性質

Promise 對象的錯誤具有 “冒泡” 性質,全部 then 的錯誤,都由下個 catch 處理,會一直傳遞下去。

(() => {
    new Promise((resolve, reject) => {
        resolve('save');
    }).then(value => {
    }).then(value => {
        throw new Error('異空間錯誤 404');
    }).catch(error => {
        console.log(error);  // Error: 異空間錯誤 404
    });
})();

# Promise 體內體外 錯誤

Promise 體內拋錯,不會拋出到體外,更不會報錯。

(() => {
                                 // 體內拋錯,不進行 Promise catch
    new Promise((resolve, reject) => {
        throw new Error('異空間錯誤 404');
    });

                                 // 體外拋錯
                                 // 由於 Promise 是立即執行的
                                 // 所以,延時 677 後的拋錯,Promise 已經執行完了,屬於體外拋錯
    new Promise((resolve, reject) => {
        resolve('save');
        setTimeout(() => {
            try {
                throw new Error('異空間錯誤 404');
            } catch (e) {
                console.log(e);  // Error: 異空間錯誤 404
            }
        }, 677);
    });
})();

# Promise.all

Promise.all 方法用於將多個 Promise 實例,包裝成一個新的 Promise 實例。

Promise.all 方法接收一個 數組 作爲參數,數組的元素都是 Promise 對象。
不是的話,需要先轉爲 Promise 對象。

只要數組內 Promise 的狀態全部都爲 resolve
Promise.all 得到的 Promise 纔會是 resolve

只要數組內的 Promise 的其中一個狀態爲 reject
Promise.all 得到的 Promise 就會是 reject

(() => {
    function getRequestById(id) {
        const BASE_URL = 'https://www.camnter.com/topic/'
        return new Promise((resolve, reject) => {
            if (typeof id === 'number' && Number(id) > 0) {
                resolve(BASE_URL + id);
            } else {
                reject(new Error('Id is not legal'));
            }
        });
    };

    function getResponseByRequest(request) {
        return new Promise((resolve, reject) => {
            if (null !== request && '' !== request) {
                resolve(400);                      // 假如成功了
            } else {
                reject(new Error('Request is not legal'));
            }
        });
    };

    let arrayA = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    Promise.all(arrayA.map(element => {
        return getRequestById(element);
    })).then(requestArray => {
        console.log(requestArray);                  //  [ 'https://www.camnter.com/topic/1', ..., ... ]
        return Promise.all(requestArray.map(request => {
            return getResponseByRequest(request);
        }));
    }).then(responseCodeArray => {
        console.log(responseCodeArray);             // [ 400, ..., ... ]
    }).catch(error => {
        console.log(error);
    });


    let arrayB = [-1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  // 假如有一個 id 不合法( -1 )
    Promise.all(arrayB.map(element => {
        return getRequestById(element);
    })).then(requestArray => {
        console.log(requestArray);
        return Promise.all(requestArray.map(request => {
            return getResponseByRequest(request);
        }));
    }).then(responseCodeArray => {
        console.log(responseCodeArray);
    }).catch(error => {
        console.log(error);                         // Error: Id is not legal
    })
})();

# Promise.race - 網絡請求超時

Promise.race 方法也是用於將多個 Promise 實例,包裝成一個新的 Promise 實例。

Promise.all 的區別在於:只要 Promise 數組中,有一個 Promise 最先改變狀態,那麼 Promise.race 返回的 Promise 實例。

/**
 * 實例:網絡請求超時
 */
(() => {
    function getResponse() {
        return new Promise((resolve, reject) => {
            setTimeout(() => resolve(), 2000);                          // 假如網絡請求需要 2000ms 才返回
        });
    };

    Promise.race(
        [
            getResponse(),
            new Promise((resolve, reject) => {
                setTimeout(() => reject(new Error('異空間超時')), 1677);  // 1677ms 就超時
            })
        ]
    ).then(value => {
        console.log(value);
    }).catch(error => {
        console.log(error);                                             // Error: 異空間超時
    });
})();

# Promise.resolve

Promise.resolve 可以 將現有的對象,轉換爲 Promise 對象。
Promise.resolve 也是一個簡版的 Promise
Promise 實例,那麼 Promise.resolve 將不做任何修改、原封不動地返回這個實例。

(() => {
    const SAVE = 'save';

    Promise
        .resolve(SAVE)
        .then(value => {
            console.log(value);  // save
        });

    // 等價於

    new Promise((resolve, reject) => {
        resolve();
    }).then(value => {
    });
})();

Promise.resolve 的參數是一個 thenable 對象。
thenable 對象指的是具有 then 方法的對象。

Promise.resolve 方法會將 thenable 對象轉爲 Promise 對象。
然後就立即執行 thenable 對象的 then 方法。

(() => {
    const SAVE = 'save';
    let thenable = {
        then(resolve, reject) {
            resolve(SAVE);
        }
    };
    Promise
        .resolve(thenable)
        .then(value => {
            console.log(value);  // save
        });
})();

/**
 * 參數不是具有 then 方法的對象
 *
 * Promise.resolve 方法會返回一個新的 Promise 對象,狀態設置爲 resolved
 */
(() => {
    Promise
        .resolve('save')
        .then(value => {
            console.log(value);  // save
        })
})();

# Promise 事件序列

(() => {
    setTimeout(function () {     // setTimeout(fn, 0) 在 下一輪 "事件循環" 開始時執行
        console.log("C");
    }, 0);


    Promise                      // Promise.resolve() 在本輪 "事件循環" 結束時執行
        .resolve()
        .then(() => {
            console.log("B");
        }).catch(error => {

    });
    console.log("A");            // console 立即執行,最先輸出
                                 // A
                                 // B
                                 // C
})();

# Promise.reject

Promise.reject 可以 將現有的對象,轉換爲 rejected 狀態的 Promise 對象。

(() => {
    const ERROR_MSG = '異空間錯誤 404';
    Promise
        .reject(ERROR_MSG)
        .catch(error => {
            console.log(error);             // 異空間錯誤 404
        });

    // 等價於

    new Promise((resolve, reject) => {
        reject(ERROR_MSG);
    }).catch(error => {
        console.log(error);                 // 異空間錯誤 404
    });
})();

Promise.reject 的參數是一個 thenable 對象。
那麼在 catch 方法中捕獲的就是 thenable 對象,不是 reject 傳下來的數據。

(() => {
    const ERROR_MSG = '異空間錯誤 404';
    const thenable = {
        then(resolve, reject) {
            reject(ERROR_MSG);
        }
    };

    Promise
        .reject(thenable)
        .catch(error => {
            console.log(error === thenable);  // true
        });
})();

# Promise 擴展方法 - done

Promise 的結尾 then 或者 catch。如果繼續拋出異常的話,是不會被捕獲到的,因爲 Promise 的異常不會拋出到外部。

需要一個方法在回調鏈的尾端,保證拋出任何可能出現的錯誤,利用 setTimeout 拋出一個全局錯誤。

/**
 * done 源碼
 */
Promise.prototype.done = function (resolve, reject) {
    return this
        .then(resolve, reject)
        .catch(error => {
            setTimeout(() => {
                throw error;
            }, 0);
        });
};

/**
 * done 使用
 */
(() => {
    Promise
        .resolve('Save')
        .then(value => {
            return value + " you from anything";
        })
        .then(value => {
            throw new Error('異空間錯誤 404');  // 異空間錯誤 404
        })
        .done();
})();

# Promise 擴展方法 - finally

不管是 resolve,還是 reject,都會執行的方法。

原理是拿到 Promise,添加一次 then
resolve 的 時候 resolve + then
reject 的 時候 resolve + then

總之就是: 強行 resolve + then

/**
 * finally 源碼
 */
Promise.prototype.finally = function (callback) {
    let constructor = this.constructor;
    return this.then(value => {
        constructor.resolve(callback()).then(() => value);
    }, error => {
        constructor.resolve(callback()).then(() => {
            throw error
        });
    })
};


(() => {
    Promise
        .resolve('Save')
        .then(value => {
            return value + " you from anything";
        })
        .then(value => {
            throw new Error('異空間錯誤 404');
        })
        .done()
        .finally(() => {
            console.log("[promise # finally]");  // [promise # finally]
        });
})();

# Promise 應用 - 加載圖片

(() => {
    const preloadImagePromise = function (path) {
        return new Promise((resolve, reject) => {
            let image = new Image();
            image.onload = resolve;
            image.error = reject;
            image.src = path;
        });
    };
})();

# Promise 應用 - Generator

/**
 * 當 Generator 遇到異步操作的時候,都會返回一個 Promise 對象
 */
(() => {
    function getSave() {
        return Promise.resolve('Save');
    };

    let generator = function*() {
        try {
            let save = yield getSave();
            console.log(save);
        } catch (e) {
            console.log(e);
        }
    };

    function run(generator) {
        let iterator = generator();

        function start(result) {
            if (result.done)return result.value;

            return result.value.then(value => {
                return start(iterator.next(value))
            }, error => {
                return start(iterator.throw(error));
            })
        }

        start(iterator.next());
    };
    run(generator);  // Save
})();



Iterator

作用:

1. 是爲各種數據結構,提供一個統一的、簡便的訪問接口。
2. 是使得數據結構的成員能夠按某種次序排列。
3.ES6 創造了一種新的遍歷命令 for...of 循環,Iterator 接口主要供 for...of 消費。


遍歷過程:

1. 創建一個指針對象,指向當前數據結構的起始位置。也就是說,遍歷器對象本質上,就是一個指針對象。
2. 第一次調用指針對象的 next 方法,可以將指針指向數據結構的第一個成員。
3. 第二次調用指針對象的 next 方法,指針就指向數據結構的第二個成員。
4. 不斷調用指針對象的 next 方法,直到它指向數據結構的結束位置。

每一次調用 next 方法,都會返回數據結構的當前成員的信息。
具體來說,就是返回一個包含 valuedone 兩個屬性的對象。
其中,value 屬性是當前成員的值,done 屬性是一個布爾值,表示遍歷是否結束。


ES6 中,有些數據結構原生具備 Iterator 接口,就可以被 for...of 循環遍歷。

原因在於: 這些數據結構原生部署了 Symbol.iterator 屬性。
另外一些數據結構沒有。凡是部署了 Symbol.iterator 屬性的數據結構,就稱爲部署了遍歷器接口。


# 原生 Iterator 接口

(() => {
    let array = ['save', 'you', 'from', 'anything'];
    let iterator = array[Symbol.iterator]();
    for (let i = 0; i < array.length; i++) {
        console.log(iterator.next());  // { value: 'save', done: false }
    }                                  // { value: 'you', done: false }
                                       // { value: 'from', done: false }
                                       // { value: 'anything', done: false }
})();

# Iterator 應用 - 對象 for…of

Symbol.iterator 返回的必須是 遍歷器對象 Iterator

(() => {
    let target = {
        data: [],
        [Symbol.iterator](){
            const self = this;
            let index = 0;

            function next() {
                if (index < self.data.length) {
                    return {
                        value: self.data[index++],
                        done: false
                    }
                } else {
                    return {
                        value: undefined,
                        done: true
                    }
                }
            };

            let iterator = {
                next: next
            };
            return iterator;
        }
    }
    let array = ['save', 'you', 'from', 'anything'];
    target.data.push(...array);    // { data: [ 'save', 'you', 'from', 'anything' ] }
    console.log(target);
    for (let targetValue of target) {
        console.log(targetValue);  // save
    }                              // you
                                   // from
                                   // anything
})();

# Iterator 應用 - 指針結構

(() => {
    function Pointer(value) {
        this.value = value;
        this.next = null;
    }

    Pointer.prototype[Symbol.iterator] = function () {
        let iterator = {
            next: next
        };
        let current = this;

        function next() {
            if (current) {
                let value = current.value;
                current = current.next;
                return {
                    value: value,
                    done: false
                };
            } else {
                return {
                    done: true
                };
            }
        }

        return iterator;
    }

    let save = new Pointer('save');
    let you = new Pointer('you');
    let from = new Pointer('from');
    let anything = new Pointer('anything');

    save.next = you;
    you.next = from;
    from.next = anything;

    for (let value of save) {
        console.log(value);  // save
    }                        // you
                             // from
                             // anything
})();

# Iterator 應用 - 引用現成的 Iterator 接口

(() => {
    let target = {
        0: 'save',
        1: 'you',
        2: 'from',
        3: 'anything',
        length: 6,
        [Symbol.iterator]: Array.prototype[Symbol.iterator]
    };
    for (let value of target) {
        console.log(value);  // save
    }                        // you
                             // from
                             // anything
                             // undefined
                             // undefined


    let errorTarget = {      // 必須對應數組的結構的 對象纔可以,普通對象不可以
        save: 'save',
        you: 'you',
        from: 'from',
        anything: 'anything',
        length: 4,
        [Symbol.iterator]: Array.prototype[Symbol.iterator]
    };
    for (let value of errorTarget) {
        console.log(value);  // undefined
    }                        // undefined
                             // undefined
                             // undefined
})();

# Iterator 應用 - Iterator + 解構解析

解構解析
默認調用 Symbol.iterator

(() => {
    let set = new Set().add('save').add('you').add('from').add('anything');
    let [save, you, from, anything] = set;
    let [first, ...second] = set;
    console.log(save);      // save
    console.log(you);       // you
    console.log(from);      // from
    console.log(anything);  // anything
    console.log(first);     // save
    console.log(second);    // [ 'you', 'from', 'anything' ]
})();

# Iterator 應用 - Iterator + 擴展運算符

擴展運算符
let array = [...iterator]
默認調用 Symbol.iterator

(() => {
    let you = 'you';
    console.log([...you]);          // [ 'y', 'o', 'u' ]
    console.log(['Save', ...you]);  // [ 'Save', 'y', 'o', 'u' ]
})();

# Iterator 應用 - 倒遍歷

(() => {
    let save = new String('Save');  // 不能寫 let save = 'Save'
    console.log([...save]);         // [ 'S', 'a', 'v', 'e' ]
    save[Symbol.iterator] = function () {
        let iterator = {
            next: next
        };
        let current = this;
        var index = current.length;

        function next() {
            if (index <= 0) {
                return {
                    done: true
                }
            } else {
                return {
                    value: current[--index],
                    done: false
                }
            }
        }

        return iterator;
    }
    console.log([...save]);         // [ 'e', 'v', 'a', 'S' ]
})();

# Iterator 應用 - Iterator + Generator

(() => {
    let generator = {};
    generator[Symbol.iterator] = function *() {  // Symbol.iterator 可以直接寫 generator 函數
        yield 2;
        yield 3;
        yield 3;
    };
    console.log([...generator]);                 // [ 2, 3, 3 ]

    // 簡潔寫法
    let smartGenerator = {
        *[Symbol.iterator](){
            yield 'save';
            yield 'you';
            yield 'from';
            yield 'anything';
        }
    };
    for (let value of smartGenerator) {
        console.log(value);                       // save
    }                                             // you
                                                  // from
                                                  // anything
})();



Generator


Generator 函數是 ES6 提供的一種異步編程解決方案。

Generator 函數是一個狀態機,封裝了多個內部狀態。
執行 Generator 函數會返回一個遍歷器對象,Generator 函數除了狀態機,還是一個遍歷器對象生成函數。
返回的遍歷器對象,可以依次遍歷 Generator 函數內部的每一個狀態。


Generator 函數是一個普通函數,但是有兩個 特徵:
function 關鍵字 與 函數名 之間有 星號
yield 語句,定義不同的內部狀態。


# Generator 基本

(() => {
    function* saveGenerator() {
        yield 'save';
        yield 'you';
        yield 'from';
        return 'anything'
    };

    let save = saveGenerator();  // 調用方法生成 Iterator 對象
    console.log(save.next());    // { value: 'save', done: false }
    console.log(save.next());    // { value: 'you', done: false }
    console.log(save.next());    // { value: 'from', done: false }
    console.log(save.next());    // { value: 'anything', done: true }
    console.log(save.next());    // { value: undefined, done: true }

    save = saveGenerator();
    for (let value of save) {    // 是 Iterator 就能用 for...of,return 不返回值
        console.log(value);      // save
    }                            // you
                                 // from
})();

# Generator 惰性求值

Generator 返回的 Iterator 之所有調用 next 方法纔會進入到下一個內部狀態。
是因爲: 提供了一種可以暫停執行的函數,yield 語句就是暫停標記。

注意: 是調用 next 才執行 yield 後面的語句,而不是 next 執行 yield ???
yield 也可以實現 “惰性求值” 的語法功能。

(() => {
    function* lazyLoading() {
        yield 2200 + 33;
    };
    console.log(lazyLoading().next());  // { value: 2233, done: false }
})();

# Generator 與 Iterator

Generator 爲什麼能和 Symbol.iterator 組合?

因爲: Symbol.iterator 方法,必須是一個生成 Iterator 的方法,Generator 函數就是 Iterator 生成函數。所以 可以作爲 Symbol.iterator 的 方法。

(() => {
    let target = {};
    target[Symbol.iterator] = function*() {
        yield 'save';
        yield 'you';
        yield 'from';
        yield 'anything'
    };
    for (let value of target) {
        console.log(value);  // save
    }                        // you
                             // from
                             // anything
})();

Generator 執行後,返回一個 Iterator 對象。
這個 Iterator 對象也有 Symbol.iterator 屬性。
執行這個 Symbol.iterator 屬性上的方法後,得到自己。

(() => {
    let generator = function*() {
    }
    let iterator = generator();
    console.log(iterator[Symbol.iterator]() === iterator);  // true
})();

# Generator next

yield 語句本身沒有 具體返回值,一直返回 undefined
next 方法可以加一個參數,作爲上一次 yield 的返回值。

(() => {
    function* resetTraverse() {
        for (let i = 1; true; i++) {
            let reset = yield i;
            if (reset) {
                i = -1;
            }
        }
    }

    let generator = resetTraverse();
    for (let i = 0; i < 2; i++) {
        console.log(generator.next());  // { value: 1, done: false }
    }                                   // { value: 2, done: false }
    console.log(generator.next(true));  // 設置 reset,i = -1,最後 i++ = 0,{ value: 0, done: false }
    for (let i = 0; i < 2; i++) {
        console.log(generator.next());  // { value: 1, done: false }
    }                                   // { value: 2, done: false }
})();

複雜例子

(() => {
    function* func(v) {
        let x = 2 * (yield (v + 1));
        let y = yield (x / 3);
        return (v + x + y);
    }

    let generatorA = func(2);
                                      // 暫停到 yield (v + 1),調用後 返回 v + 1
                                      // 得到 3
    console.log(generatorA.next());   // { value: 3, done: false }

                                      // 暫停到 yield (x / 3),調用後 返回 x / 3
                                      // x = 2 * (yield (v + 1)),yield (v + 1) = undefined
                                      // x = 2 * undefined = NaN
                                      // x / 3 = NaN
    console.log(generatorA.next());   // { value: NaN, done: false }

                                      // 暫停到 return,調用後 返回 v + x + y,return 調用後結束
                                      // x = 2 * (yield (v + 1)),yield (v + 1) = undefined
                                      // x = 2 * undefined = NaN
                                      // y = yield (x / 3),yield (x / 3) = undefined
                                      // y = undefined
                                      // v + x + y = 2 + NaN + undefined = NaN
    console.log(generatorA.next());   // { value: NaN, done: true }


    let generatorB = func(2);
                                      // 暫停到 yield (v + 1),調用後 返回 v + 1
                                      // 得到 3
    console.log(generatorB.next());   // { value: 3, done: false }

                                      // 暫停到 yield (x / 3),調用後 返回 x / 3
                                      // x = 2 * (yield (v + 1)),yield (v + 1) = 3
                                      // x = 2 * 3 = 6
                                      // x / 3 = 2
    console.log(generatorB.next(3));  // { value: 2, done: false }

                                      // 暫停到 return,調用後 返回 v + x + y,return 調用後結束
                                      // x = 2 * (yield (v + 1)),yield (v + 1) = 3
                                      // x = 2 * 3 = 6
                                      // y = yield (x / 3),yield (x / 3) = 3
                                      // y = 3
                                      // v + x + y = 2 + 6 + 3 = 11
    console.log(generatorB.next(3));  // { value: 11, done: true }
})();

# Generator 第一次 next 生效

第一次 next 的參數值是失效的。

以上面的爲例:yield (v + 1)``,調用後 返回v + 1,完全不涉及到yield (v + 1)` 的值,只關心了yield
後面是
v + 1` 的值。

如果想要第一調用 next 方法時,參數值生效。
需要在 Generator 包一層。

實質上就是完成了:
1. 創建 Generator 方法 + 手動調用一次 next
2. 然後這個參數作爲 創建 Generator 方法 時的初始值。

(() => {
    function wrapperGenerator(generatorFunction) {
        return function (...args) {
            let generator = generatorFunction(...args);
            generator.next();
            return generator;
        };
    }

    const wrapped = wrapperGenerator(function*() {
        return yield;
    });

    let generator = wrapped();
    console.log(generator.next('Save'));  // { value: 'Save', done: true }
    console.log(generator.next());        // { value: undefined, done: true }
})();

# Generator 輸入值

(() => {
    function * generatorFunction() {
        for (; ;) {
            console.log(yield);
        }
    }

    let generator = generatorFunction();
    generator.next();
    let save = 'Save';
    for (let char of save) {
        generator.next(char);  // S
    }                          // a
                               // v
                               // e
})();

# Generator 斐波那契算法

(() => {
    function* fibonacci() {
        let [previous, current] = [0, 1];
        for (; ;) {
            [previous, current] = [current, previous + current];
            yield current;
        }
    }

    for (let total of fibonacci()) {
        if (total > 1000) {
            break;
        }
        console.log(total);  // 1
    }                        // 2
                             // 3
                             // 5
                             // 8
                             // 13
                             // 21
                             // 34
                             // 55
                             // 89
                             // 144
                             // 233
                             // 377
                             // 610
                             // 987
})();

# Generator 遍歷對象

第一種寫法 - 侵入式,但是個人還是喜歡在這種。

(() => {
    let target = {
        save: 'save',
        you: 'you',
        from: 'from',
        anything: 'anything'
    };
    target[Symbol.iterator] = function*() {
        const SYMBOL_TYPE = 'symbol';
        let _this = target;
        let handler = {
            ownKeys(target){

                return Reflect
                    .ownKeys(target)
                    .filter(key => typeof key !== SYMBOL_TYPE);  // 過濾掉 Symbol.???
            }
        }
        let proxy = new Proxy(_this, handler);
        for (let key of  Object.keys(proxy)) {
            yield [key, target[key]];
        }
    }
    for (let [key, value] of target) {
        console.log("[key, value] = [", key, ", ", value, "]");   // [key, value] = [ save ,  save ]
    }                                                             // [key, value] = [ you ,  you ]
                                                                  // [key, value] = [ from ,  from ]
                                                                  // [key, value] = [ anything ,  anything ]
})();

第二種寫法 - 無侵入式

(() => {
    let target = {
        save: 'save',
        you: 'you',
        from: 'from',
        anything: 'anything'
    };

    function* generatorFunction(target) {
        const SYMBOL_TYPE = 'symbol';
        let _this = target;
        let handler = {
            ownKeys(target){
                return Reflect
                    .ownKeys(target)
                    .filter(key => typeof key !== SYMBOL_TYPE);  // 過濾掉 Symbol.???
            }
        }
        let proxy = new Proxy(_this, handler);
        for (let key of  Object.keys(proxy)) {
            yield [key, target[key]];
        }
    }

    for (let [key, value] of generatorFunction(target)) {
        console.log("[key, value] = [", key, ", ", value, "]");   // [key, value] = [ save ,  save ]
    }                                                             // [key, value] = [ you ,  you ]
                                                                  // [key, value] = [ from ,  from ]
                                                                  // [key, value] = [ anything ,  anything ]
})();

# Generator throw

Generator 函數返回的遍歷器對象,都有一個 throw 方法。
可以在函數體外拋出錯誤,然後在 Generator 函數體內捕獲。

(() => {
    let generatorFunction = function*() {
        try {
            yield;
        } catch (e) {
            console.log("[內部捕獲] = ", e);
        }
    };
    let generator = generatorFunction();
    generator.next();

    try {
        generator.throw('save');  // [內部捕獲] =  save
        generator.throw('you');   // [外部捕獲] =  you
    } catch (e) {
        console.log("[外部捕獲] = ", e);
    }
})();

Generator.throw 可以接受參數,還會被 Generator catch 語句接收,建議Error 對象。

(() => {
    let generatorFunction = function*() {
        try {
            yield;
        } catch (e) {
            console.log("[內部捕獲] = ", e);
        }
    };
    let generator = generatorFunction();
    generator.next();
    generator.throw(new Error('異空間錯誤:2233')); // [內部捕獲] =  Error: 異空間錯誤:2233
})();

throw 方法被捕獲後,會附帶執行下一條 yield 語句。

(() => {
    let generatorFunction = function*() {
        try {
            yield 22;
        } catch (e) {
            // Nothing to do
        }
        yield 33;
    }
    let generator = generatorFunction();
    console.log(generator.next());   // { value: 22, done: false }
    console.log(generator.throw());  // 已經執行到第二個 yield
                                     // { value: 33, done: false }
    console.log(generator.next());   // { value: undefined, done: true }
})();

如果 Generator 內部出現異常沒捕獲的話,就不會執行下去了。
下次 nextvalue = undefined,done = true

(() => {
    function* generatorFunction() {
        yield 'save';                                  // 停
        console.log("[generator console]");            // 這裏不會停
        throw new Error('');                           // 停
        yield 22;
        yield 33;
    };
    function log(generator) {
        var v;
        try {
            v = generator.next();
            console.log("[第一次 next]", v);            // [第一次 next] { value: 'save', done: false }
        } catch (e) {
            console.log("[第一次 next - 捕捉錯誤]", e);
        }
        try {
            v = generator.next();                       // [generator console]
            console.log("[第二次 next]", v);
        } catch (e) {
            console.log("[第二次 next - 捕捉錯誤]", e);  // [第二次 next - 捕捉錯誤] Error
        }
        // 後續都不執行了
        console.log("[第三次 next]", generator.next());  // [第三次 next] { value: undefined, done: true }
        console.log("[第四次 next]", generator.next());  // [第四次 next] { value: undefined, done: true }
        console.log("[第五次 next]", generator.next());  // [第五次 next] { value: undefined, done: true }
    }

    log(generatorFunction());
})();

# Generator return

Generator 方法返回的 Iterator 對象,還有一個 return 方法,可以返回給定的值。

(() => {
    function* generatorFunction() {
        yield '22';
        yield '33';
        yield '2233';
    };
    let generator = generatorFunction();      // 提供參數 return
    console.log(generator.next());            // { value: '22', done: false }
    console.log(generator.next());            // { value: '33', done: false }
    console.log(generator.return('return'));  // { value: 'return', done: true }
    console.log(generator.next());            // { value: undefined, done: true }
    console.log(generator.next());            // { value: undefined, done: true }

    generator = generatorFunction();          // 不提供參數
    console.log(generator.next());            // { value: '22', done: false }
    console.log(generator.next());            // { value: '33', done: false }
    console.log(generator.return());          // { value: undefined, done: true }
    console.log(generator.next());            // { value: undefined, done: true }
    console.log(generator.next());            // { value: undefined, done: true }
})();

finally 代碼塊的話,return 會推遲到 finally,代碼塊執行完之後才執行。

(() => {
    function* generatorFunction() {
        yield 1;
        try {
            yield 2;
            yield 3;
            yield 4;
        } finally {
            yield 5;
            yield 6;
            yield 7;
        }
        yield 8;
    };
    let generator = generatorFunction();
    console.log(generator.next());        // { value: 1, done: false }
    console.log(generator.next());        // { value: 2, done: false }
    console.log(generator.return(2233));  // { value: 5, done: false }
    console.log(generator.next());        // { value: 6, done: false }
    console.log(generator.next());        // { value: 7, done: false }
                                          // 一定要等到 finally 執行完,纔會執行 return
    console.log(generator.next());        // { value: 2233, done: true }
})();

# Generator yield*

Generator 方法內部,直接調用 Generator 是沒效果的。

(() => {
    function* generatorFunction() {
        yield 'save';
        yield 'you';
        generatorFunction();
    };
    for (let value of generatorFunction()) {
        console.log(value);  // save
    }                        // you
                             // generatorFunction() 並沒有效果
})();

yield* 就有效。

(() => {
    function* otherGeneratorFunction() {
        yield 'from';
        yield 'anything';
    };
    function* generatorFunctionA() {
        yield 'save';
        yield 'you';
        yield* otherGeneratorFunction();
    };
    for (let value of generatorFunctionA()) {
        console.log(value);  // save
    }                        // you
                             // from
                             // anything

                             // 等同於

    function* generatorFunctionB() {
        yield 'save';
        yield 'you';
        for (let value of otherGeneratorFunction()) {
            yield value;
        }
    };
    for (let value of generatorFunctionB()) {
        console.log(value);  // save
    }                        // you
                             // from
                             // anything
})();

yield Generatoryield* Generator

(() => {
    function* otherGeneratorFunction() {
        yield 2233;
    };
    /**
     * yield Generator
     */
    function* generatorFunctionA() {
        yield 'save';
        yield 'you';
        yield otherGeneratorFunction();
    };
    /**
     * yield* Generator
     */
    function* generatorFunctionB() {
        yield 'save';
        yield 'you';
        yield* otherGeneratorFunction();
    };

    let generatorA = generatorFunctionA();
    let generatorB = generatorFunctionB();

    console.log(generatorA.next().value);         // save
    console.log(generatorA.next().value);         // you
                                                  // yield Generator 返回一個 Iterator 對象
    console.log(typeof generatorA.next().value);  // object

    console.log(generatorB.next().value);         // save
    console.log(generatorB.next().value);         // you
                                                  // yield* Generator 會遍歷 Generator 返回的 Iterator 對象
    console.log(generatorB.next().value);         // 2233
})();

yield* Generator 等同於 for...of Generator

(() => {
    function* concatA(previousGenerator, afterGenerator) {
        yield previousGenerator;
        yield afterGenerator;
    };

    // 等同於

    function* concatB(previousGenerator, afterGenerator) {
        for (let value of previousGenerator) {
            yield value;
        }
        for (let value of afterGenerator) {
            yield value;
        }
    };
})();

yield* Generator 的話,會 for...of 這個 Generator
yield* Iterator 的話,也會 for...of 這個 Iterator
數組也是有自己的 Iterator
所有,yield* 數組,就會直接遍歷這個數組。

(() => {
    function* generatorFunction() {
        yield* ['save', 'you', 'from', 'anything'];
    };
    let generator = generatorFunction();
    for (let value of generator) {
        console.log(value);         // save
    }                               // you
                                    // from
                                    // anything
    generator = generatorFunction();
    console.log(generator.next());  // { value: 'save', done: false }
    console.log(generator.next());  // { value: 'you', done: false }
})();

只要有 Iterator 接口,就可以被 yield* 遍歷。

(() => {
    function* generatorFunction() {
        yield* 'Save';
    };
    for (let char of generatorFunction()) {
        console.log(char);  // S
    }                       // a
                            // v
                            // e
})();

yield* Generator return 可以作爲 yield* 返回的值。

(() => {
    function* otherGeneratorFunction() {
        yield 22;
        yield 33;
        return '[ otherGeneratorFunction ]';
    };
    function* generatorFunction() {
        yield 11;
        let returnValue = yield* otherGeneratorFunction();
        console.log(returnValue);
        yield 44;
    };
    let generator = generatorFunction();
    console.log(generator.next());  // { value: 11, done: false }
    console.log(generator.next());  // { value: 22, done: false }
    console.log(generator.next());  // { value: 33, done: false }
    console.log(generator.next());  // [ otherGeneratorFunction ]
                                    // { value: 44, done: false }
    console.log(generator.next());  // { value: undefined, done: true }
})();

# Generator 應用 - 深度遍歷

(() => {
    let array = [11, [22, 33, [44, 55, [66, 77]]], [88, 99]];

    function* depthTraversal(array) {
        if (Array.isArray(array)) {
            for (let element of array) {
                yield* depthTraversal(element);
            }
        } else {
            yield array;
        }
    };
    for (let value of depthTraversal(array)) {
        console.log(value);  // 11
    }                        // 22
                             // 33
                             // 44
                             // 55
                             // 66
                             // 77
                             // 88
                             // 99
})();

# Generator 應用 - 遍歷完全二叉樹

(() => {
    function Tree(left, current, right) {
        this.left = left;
        this.current = current;
        this.right = right;
    };
    function* inorder(node) {
        if (node) {
            yield* inorder(node.left);
            yield node.current;
            yield* inorder(node.right);
        }
    };
    function makeTree(array) {
        if (array.length == 1)return new Tree(null, array[0], null);
        return new Tree(makeTree(array[0]), array[1], makeTree(array[2]));
    };
    let tree = makeTree([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]);
    console.log(tree);      // Tree {
                            //     left:
                            //         Tree {
                            //         left: Tree { left: null, current: 'a', right: null },
                            //         current: 'b',
                            //             right: Tree { left: null, current: 'c', right: null } },
                            //     current: 'd',
                            //         right:
                            //     Tree {
                            //         left: Tree { left: null, current: 'e', right: null },
                            //         current: 'f',
                            //             right: Tree { left: null, current: 'g', right: null } } }
    for (let node of inorder(tree)) {
        console.log(node);  // a
    }                       // b
                            // c
                            // d
                            // e
                            // f
                            // g
})();

# Generator this

ES6 規定 Generator 返回的 Iterator 是 Generator 的實例。
也繼承了 Generator 方法的 prototype 對象上的方法。

(() => {
    function* generatorFunction() {
    };
    generatorFunction.prototype.save = function () {
        return 'save';
    };
    let generator = generatorFunction();
    console.log(generator instanceof generatorFunction);  // true
    console.log(generator.save());                        // save
})();

Generator 作爲普通構造函數,this 不會在 Iterator 中生效。

(() => {
    function* generatorFunction() {
        this.save = 'save';
    };
    let generator = generatorFunction();
    console.log(generator instanceof generatorFunction);  // true
    console.log(generator.save);                          // undefined
})();

Generator 作爲一個對象,又可以 next,又可以獲得正常 this
但是:對象不統一

(() => {
    function* generatorFunction() {
        this.save = 'save';
        yield this.you = 'you';
    };
    let target = {};

    let generator = generatorFunction.call(target);  // this = target
    console.log(generator.next());                   // { value: 'you', done: false }
    console.log(generator.next());                   // { value: undefined, done: true }
                                                     // this 代碼,都保存在 target 上了
    console.log(target);                             // { save: 'save', you: 'you' }
    console.log(target.save);                        // save
    console.log(target.you);                         // you
})();

Generator 作爲一個對象,又可以 next,又可以獲得正常 this
但是:對象統一

(() => {
    function* generatorFunction() {
        this.save = 'save';
        yield this.you = 'you';
    };
    let generator = generatorFunction.call(generatorFunction.prototype);  // 傳入 自身的 prototype
    console.log(generator.next());                                        // { value: 'you', done: false }
    console.log(generator.next());                                        // { value: undefined, done: true }
    console.log(generator);                                               // {}
    console.log(generator.save);                                          // save
    console.log(generator.you);                                           // you
})();

# Generator 實現 - 異步操作的同步化

(() => {
    function* loadUI() {
        showLoadingScreen();
        yield loadUIDataAsynchronously();
        hideLoadingScreen();
    };
    function showLoadingScreen() {
        console.log("[showLoadingScreen]");
    };
    function loadUIDataAsynchronously() {
        console.log("[loadUIDataAsynchronously]");
    };
    function hideLoadingScreen() {
        console.log("[hideLoadingScreen]");
    };


    let loader = loadUI();
    loader.next();          // [showLoadingScreen]
                            // [loadUIDataAsynchronously]
    loader.next();          // [hideLoadingScreen]
})();

# Generator 實現 - 逐行讀文件

(() => {
    function* readFile(filePath) {
        let file = new FileReader(filePath);
        try {
            while (!file.eof) {
                yield parseInt(file.readLine(), 10);
            }
        } catch (e) {
            console.log(e);
        } finally {
            file.close();
        }
    };
})();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章