ES6 实用点记录

ES6 实用点记录

29 April 2017

这或许是 注定 的...
这一次,将会紧紧握住,再也不放手。

复习了好多,又学习了好多,写了一本书的代码。
仅仅 记录一些 个人认为实用点







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 统一用户,原生提供了 PromisePromise 是一个对象,可以获取异步操作的消息。各种异步操作可以用同样的方法进行处理。


特点:

  • 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 的状态全部都为 resolvePromise.all 得到的 Promise 才会是 resolve

只要数组内的 Promise 的其中一个状态为 rejectPromise.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();
        }
    };
})();