| let块级作用域、 const常量
| 解构赋值
| 箭头函数、default、rest、spread
| 模板字符串
| class: extends、super、constructor、静态属性和方法、实例属性
| Promise、Generator、async/await
| 模块化: import、export、as、*、default export
| 数据结构: Set不重复值的集合、Map
| Proxy 对象的拦截器
| Symbol
| symbol 独一无二的值
| Set、Map数据结构

let和const

一、let 声明变量

    let完全可以取代var, 特性: 1、let不允许重复声明        2、没有预解析功能        3、块级作用域

    1、预解析功能

        console.log(a);        // 报错, 如果是原来的var声明这里会是undeined, 所以let没有预解析功能
        let a = 20;

    2、块级作用域 在{ }中声明的变量只能在括号里使用

        var name = 'lulu';
        function prsone(){
            let name = 'siguang';
        }

        prsone();
        console.log(name);            // 'lulu'

        // 输出的都是10
        for(var i=0; i<10; i++){
            setTimeout(function(){
                console.log(i);        // 使用var声明的i都会打印出9,如果使用let输出的就是0到10
            })
        }

二、const常量

    1、常量只允许赋一次值不能被修改        2、常量声明都为大写        3、与let相当只在块级作用域有效

    const NAME = 'siguang';        // 不能变化的值
    NAME = 'lulu';                // 报错

    if (true) {
        const MAX = 5;
    }
    MAX            // 报错,const与let相同都是在块内使用

解构赋值

允许从数组和对象中提取的值,对变量进行赋值,被称为解构赋值

一、数组解构使用[]

    let [a, b, c] = [1, 2, 3];    // a=1, b=2, c=3

    // 默认值
    let [x, y = 'b'] = ['a'];     // x='a', y='b'


二、对象解构使用{}

    // 对象是按名字来解析赋值
    let obj = {
        getName: function(){}, 
        foo: 'aaa', 
        bar: 'bbb'
    };
    let {foo, bar} = obj        // foo='aaa', bar='bbb'

    // 对象的解构赋值,先找到同名,在将值赋给对应的变量
    let { foo: val, bar } = { foo: "aaa", bar: "bbb" };  
    console.log(val, bar)        // foo='undefined', val='aaa', bar='bbb'


三、字符串的解构赋值

    const [a, b, c, d, e] = 'hello';    // a=h, b=e, c=l, d=l, e=o
    const [...tail] = 'hello';            // ['h', 'e', 'l', 'l', 'o']

    # length属性
    let {length: len} = 'hello';        // length = 5


四、默认参数 default

    # 参数作为数组
    function add([x, y]){
        return x * y
    }
    add([3,2]);        // 6

    # 参数作为对象,对x、y的默认初始值
    function move({x = 0, y = 0}) {
        return [x, y];
    }
    move({x: 3, y: 8}); // [3, 8] 


五、不定参数 Rest

    function f(x, ...y) {
        // y是一个数组
        return x * y.length;
    }
    f(3, "hello", true) == 6


六、扩展运算符 Spread

    function f(x, y, z) {
        return x + y + z;
    }
    // 将数组中的每个元素展开为函数参数
    f(...[1,2,3])            // 6

模板字符串

一、写到`...`之间,值的输出使用 ${...} 

二、可以写多行字符串,只写到`...`之间就可以

三、${...}可以解析变量、对象的值、表达式、函数返回

    let name = 'siguang';
    let obj = {
        age: 33
    }
    console.log(`你的名字是: ${ name } 你的年龄是:  ${ obj.age }`)

四、标签模板

    let a=10, b=20;
    dialog`Hello ${a+b} world ${a*b}`;        // 等同于 dialog()

函数扩展

一、箭头函数

    1、箭头函数的注意问题: 

        * 箭头函数里的this不是指向调用者,而指向对象
        * 不能当构造函数来用, 不能使用new
        * 函数内不存在arguments对象
        * 不可以使用yield命令,箭头函数不能用作Generator函数

        arr.sort(function(){  ...  }) 相当于 arr.sort((a, b) => a-b);

        // 箭头函数的this,不是指向调用者
        const Template = {
            test(){
                console.log(this);        // this指向Template

                document.querySelector("#showThis").onclick = () =>{
                    /* 如果非箭头函数this应该指向 #showThis */
                    /* 这里箭头函数不是指向调用者,所以指向了 Template */
                    console.log(this);    
                }
            }
        }
        Template.test();

    2、var f = function(v){ return v; }

        // ES6写法
        var f = v => v;

    3、var sum = function(num1, num2){
            return num1 + num2;
        }

        // ES6写法
        var sum = (num1, num2) => {
            return num1 + num2;
        }

    4、对象中方法的简写

        var obj = {
            name: 'haha',
            getName(){                // 是getName: function(){}的简写
                console.log(this.name);
            },
            setName(name){
                this.name = name;
                console.log(this.name);
            }
        }


二、函数参数默认值

    function fn(a, b = 2){
        // b如果不传相当于2,等同于 var b = arguments.length > 1 && arguments[1] == undefined ? arguments[1] : 2;
        return {a, b}
    }
    console.log(fn(10));        // 返回{a: 10, b: 2}

    // 如果没有参数默认值时还需要在函数体内做兼容
    function fn(name){
        var getName = name || 20;
    }
    fn();


三、参数的解构赋值

        const full = ({ first, last }) => first + ' ' + last;    
        full({first: 20, last: 30})

        // 等同于
        // function full(person) {
        //      return person.first + ' ' + person.last;
        // }


四、rest 获取多余参数

    以'...变量名' 来获取多余的参数,返回一个数组,这样就不需要arguments对象了

    function person(...rest){
        console.log(rest);        // [1,2,3,4,5]
    }
    person(1,2,3,4,5);

    function person(a, ...rest){
        console.log(a);            // 1
        console.log(rest);        // [2,3,4,5]
    }
    person(1,2,3,4,5);


五、spread 扩展操作符 ...  与rest相反

    function add(x, y) {
        return x + y;        // 4+38
    }
    var numbers = [4, 38];            // 只能为数组不能为对象
    add(...numbers)         // 42

    # 可以将字符串转成数组
    let arr = [...'hello'];        // ['h', 'e', 'l', 'l', 'o']


六、name属性

    funciton foo(){ ... }
    foo.name;        // 'foo' 返回函数名


七、::运算符

    箭头函数可以绑定this对象,减少显式this对象的写法(call、apply、bind)

    ES7提出了函数绑定来取代"call、apply、bind"调用,使用两个"::"

    foo::bar;        // 等同于 bar.bind(foo);
    foo::bar(...arguments);        // 等同于 bar.apply(foo, arguments);

类、继承

一、类的一些特性

    1、对象方法的简写方法
        class Cat{
            getName(){        // 老写法 getName: function(){}
                ...
            }
        }

    2、通过__proto__属性可以直接调用父类
        var parent = {
            getParentName(){
                console.log('parent')
            }
        }

        var childer = {
            __proto__: parent,            // 通过__proto__指向parent, 就可以直接调用parent类中的方法
            getChildeName(){
                console.log('childer');
            }
        }

        childer.getChildeName();    // childer
        childer.getParentName();    // parent


二、创建类

    class Cat {
        // ES6中新型构造器, 用来初始化时这里接收参数
        constructor(name){ 
            this.name = name;
        }

        getName(){        // 公有方法
            console.log(this.name);
        }

        static bar(baz){        // 私有方法
            debugger;
            return this.name = baz;
        }

        * gen(){
            let arg = 10;
            yield arg;
        }
    }

    Cat.userName = 'siguang';

    var oCat = new Cat('哈哈');
    oCat.getName();
    oCat.bar('lulu');                // 报错静态方法调用不到,并且不会被继承
    oCat.gen().next()


三、继承 extends

    // 创建一个类
    class Person {
        constructor(name){
            this.name = name;
        }

        getName(){
            console.log(this.name);
        }
    }

    // 继承这个类
    class Children extends Person {
        constructor(name, color){
            super(name);                // super指向继承的构造函数Person的constructor
            this.color = color;
        }

        getColor(){
            console.log(this.name, this.color);
        }
    }

    // 实例化
    var op = new Person('siguang');
    op.getName();            // 'siguang'

    var oc = new Children('lulu', 'red');        
    oc.getColor();        // 'red'
    oc.getName();            // 'lulu'


四、static 静态属性和方法

    类似构造函数,直接挂载到函数下的叫对象的属性和方法,而写到构造函数内部的叫构造函数的属性和方法

    1、静态方法前面需要加上'static'关键字,相当于只是函数下的方法而不是prototype的方法,静态方法可以被子类继承,

        class Foo{
            static methodName(){
                return 'hello'
            }
        }
        Foo.methodName();    // hello

        var foo = new Foo();
        foo.methodName();    // 报错

    2、静态属性

        ES6中没有静态属性,只能声明静态方法,ES7中可以直接写到类内在进行转码

        class Foo {
            constructor(...arg){
                this.args = args;
            } 
            outputUserName(){
                console.log(this.username);    // 这里username未定义,除非在constructor中定义 this.username
            }
        }
        Foo.username = 'siguang';       // 类的静态属性

        var foo = new Foo();
        console.log(foo.personName);  // undefined
        foo.outputUserName();               // undefined 


五、实例属性

    注意: 一定要将实例属性和静态属性区分,实例属性写到constructor中this.username, new后的实例时候可以被实例方法调用,而静态属性只挂到了类下直接通过类来调取,Foo.username

    class Foo{
        constructor(){
            this.username = 'siguang';
        }
        showName(){
            console.log(this.username);     // siguang
            console.log(Foo.username);      // lulu
        }
    }
    Foo.username = 'lulu';

    var foo = new Foo();
    foo.showName();

    ****** ES7 中静态属性和实例属性的定义 ******
    class MyClass{
        usernameA = 'siguang';        // 实例属性

        showName(){
            console.log(this.usernameA);
        }
    }


六、super()方法

    继承时必须调用一次super方法,否则constructor中的this为undefined,调用父类的构造器进行初始化, 子类调用父类的构造函数

    class BaseModel {
        constructor(options, data) { // class constructor,node.js 5.6暂时不支持options = {}, data = []这样传参
            this.name = 'Base';
            this.url = 'http://azat.co/api';
            this.data = data;
            this.options = options;
        }
        getName() { // class method
            console.log(`Class name: ${this.name}`);
        }
    }

    class AccountModel extends BaseModel {
        constructor(options, data) {
            super({private: true}, ['3333', '4444']); 
            this.name = 'Account Model';
            this.url +='/accounts/';
        }
        getAccountsData() {
            return this.data;
        }
    }

    let accounts = new AccountModel(5);
    accounts.getName();         // Account Model
    console.log('Data is %s', accounts.getAccountsData);        // {private: true}

    let base = new BaseModel({public: true}, ['111', '2222']);
    console.log(base.getName());        // Base


七、class中的Generator方法

    方法前面加" * " 号表示该方法是Generator函数

    class MyClass = {
        constructor(){
            ...
        }

        * getName(){
            ...
        }
    }

Module 模块

一、import: 模块加载

    // 取整个对象
    import $ from 'jquery';

    // 通过解构取对象
    import {name1, name2, nam3} from './user.js';

    // 通过逗号分两整体和解构分别来取
    import React, { Component, PropTypes } from 'react';

    // 重命名
    import * as React from 'react';        // * 对整体模块加载,并通过as转换一个名

    // 按需加载
    button.addEventListener('click', event => {
        imoprt('dialog.js')
            .then(dialog => {
                dialog.show();
            }).
            catch(err =>{
                // error
            })
    }, false)


    不同写法

        1、普通写法

            meat.js
                export function beef(){
                    return '牛肉';
                }
                export function pork(){
                    return '猪肉';
                }

            main.js

                import { beef, pork } from 'meat.js';
                console.log(beef());        // 牛肉
                console.log(pork());        // 猪肉


        2、key的简写

            var fn1 = function(){
                console.log('fn1');
            }
            var fn2 = function(){
                console.log('fn2');
            }

            export {fn1, fn2}         // 对象名与key相同可以写成一个


二、export: 模块输出

    1、使用对象简写方式

        var name1 = 'aaa';
        var name2 = 'bbb';
        var name3 = 'ccc';
        export {name1, name2, name3} 

        // 也可以写成    
        export var name1 = 'aaa';
        export var name2 = 'bbb';
        export var name3 = 'ccc';


    2、可以直接输出变量、函数或类

        export function person(){ ... }

        // 不能直接输出变量名
        var name = 'siguang';
        export name;            // 报错

        写成: export var name = 'siguang';        
        写成: var name = 'siguang';   export {name};    


    3、跨模块常量

        const常量只能在当前代码下使用,通过export可以进行跨模块常量,多个文件都可以使用

        export const name = 'siguang';
        export const age = 33;

        import * as info from 'userinfo';        // 或 import { name, age } from 'userinfo'


三、as 关键字来修改名字

    meat.js

        export function beef(){
            return '牛肉';
        }

        export function pork(){
            return '猪肉';
        }

    main.js

        // * 代表meat.js下所有的对外接口,转换成meat对象下,这样就可以通过meat来调用meat下的
        import * as meat from './meat.js';        
        console.log(meat.beef());        // 牛肉
        console.log(meat.pork());        // 猪肉


四、* 将somModule内的所有导出接口

    import * as newSome from './someModule'        // 将someModule类的所有接口更改到newSome下


五、export default指定匿名

    不使用default在import加载定义名与导出名必须相同,否则无法加载,如果指定默认输出使用export default,在import导入的时候不用在关心命名的问题

    1、meat.js

        // 倒出匿名函数
        export default function() {
            console.log('foo');
        }

        简写
        export default(){        // export default {}  写成默认对象

        }

    main.js

        // 导入时可以任意起名
        import beef from './meat.js'


    2、default与不写default输出的区别

        // 使用default来输出,import不需要使用{}
        export default function crc32() {
            // ...
        }
        import crc32 from 'crc32';

        // 不使用default来输出,import需要使用 {}
        export function crc32() { // 输出
            // ...
        };
        import {crc32} from 'crc32'; // 输入

Set、Map数据结构

一、Set(): 类似数组的一种新结构,成员都是唯一的值没有重复值,相当于数组去重

    # 数组去重的新方法
    let setValue = new Set([1,2,33,1,22,2,2,4,2,1,2])
    console.log(setValue, Object.prototype.toString.call(setValue));        // {size: 5, [1, 2, 33, 22, 4]}, '[Object Set]'
    let arr = [...setValue];        // [1, 2, 33, 22, 4] 需要进行解构

    特性: 
        1、不允许数组里有重复数据

    方法: 
        1、set.size: 获取数据的长度

        2、set.add(value): 添加值

        3、set.delete(value): 删除set的实例值

        4、set.has(value): 传入的参数是否为set的成员

        5、set.clear(): 删除set的所有成员


二、Map(): 将二维数组转成对象

    var map = new Map([['username', 'siguang'], ['password', 'ssssss']]);
    map.get('username');        // siguang

    /* new Map([['usernmae', 'aaa', 'password', 'ssssss'], ['aaaa', 1111]]);  注意一个数组中只有两个下标的值,这里只能get到username, password不会取到 */

    特性: 不允许有重复的key值

    方法: 
        1、map.size: 成员总数

        2、map.set('key', 'value'): 添加成员

        3、map.get(): 获取

        4、map.clear(): 清除所有

对象扩展 Object

一、简写

    1、对象属性和值相同可以写成一个

        var foo = { name: 'siguang'};
        var baz = {
            foo            // foo: foo 相同
        };
        console.log(baz.foo.name);    // siguang

    2、对象中函数简写

        var o = {
            name: 'momo',
            getName(){            // 等同于 getName: function(){}
                retrun this.name;
            }
        }

    3、参数作为对象返回

        function f(x, y) { return {x, y}; }

        // 等同于
        function f(x, y) {
            return {x: x, y: y};
        }
        f(1, 2)     // Object {x: 1, y: 2}

        // 2、CommonJS模块输出变量
        const getItem = function(){ ... }
        const setItem = function(){ ... }
        const clear = function(){ ... }
        module.exports = { getItem, setItem, clear };

        // 等同于
        // module.exports = {
        //   getItem: getItem,
        //   setItem: setItem,
        //   clear: clear
        // };


二、[] 属性名表达式

    // 对象的属性名可以用'[]'字符串拼接
    var sex = '男';
    var obj = {
        name: 'momo',
        [sex]: false,
        ['get'+'Name'](){
            console.log(this.name, this['男']);        // 返回 momo false
        }
    }
    obj.getName();


三、getter 赋值器、setter 取值器

    let cat = { 
        name: '喵喵', 
        get name(){ return this.name },   
        set name(value){
            if(Object.prototype.toString.call('xxx') == '[object String]'){
                this.name += value
            }
        }
    }


四、Object.is(): 用于对比两个数字或字符串是否相等,相当于 === ,可以正确比较 -0和0,NaN和

    '=='与'==='的缺点:

        相等运算符(==)缺点: 自动转换数据类型
        和严格相等运算符(===)缺点: NaN不等于自身,以及+0等于-0

    Objetc.is(0, -0);        // false
    Object.is(NaN, NaN);    // true


五、Object.assign(目标对象, 被合并对象B, 被合并对象C): 合并对象

    /* 注意: 被合并对象与合并目标对象的属性相同会赋盖掉前面的对象的值 */
    var objA = {a: 1, b:2}, objB = {b: 3, c: 4}; 
    let req = Object.assign(objA, objB);         // {a:1, b:3, c:4}

    添加对象的方法: 
        Object.assign(SomeClass.prototype, {
            someMethod(arg1, arg2){
                // ...
            },
            anotherMethod(){
                // ...
            }
        })

        相当于
        SomeClass.prototype.someMethod = function(arg1, arg2){ // ... }
        SomeClass.prototype.anotherMethod = function(){ // ... }

    克隆对象: 
        function clone(obj){
            return Object.assign({}, obj);
        }

    注意点: 

        1、assign 是浅拷贝

        2、同名属性替换


六、getPrototypeOf(object)、setPrototype(object, 谁的prototype): 获取、设置对象的prototype

    var Cat = function(name){
        this.name = name;
    }

    Cat.prototype.showName = function(){
        return this.name;
    }

    var c1 = new Cat('momo');
    c1.showName();

    console.log(Object.getPrototypeOf(c1))

    // 设置prototype
    var Person = function(nationality){
        this.nationality = nationality;
    }
    Person.prototype = {
        showNationality: function(){
            return this.nationality;
        }
    }
    Object.setPrototypeOf(c1, Person.prototype);
    console.log(Object.getPrototypeOf(c1));


七、keys()、values()

    获取对象的所有keys或所有值, 并返回一个数组

    var obj = {name: 'siguang', age: 33};
    console.log(Object.keys(obj));        // [name, age]
    console.log(Object.values(obj));    // ['siguang', 33]


八、__proto__属性

    用来读取或设置当前对象的prototype对象

    var Person = function(name){
        this.name = name;
    }
    Person.prototype = {
        constructor: Person,
        getName(){
            console.log(this.name);
        }
    }
    var op = new Person('lulu');

    // 继承
    var PersonChilder = function(){}; 
    PersonChilder.__proto__ = op;
    var opc = new PersonChilder(); 
    console.log(opc.getName())

    增强的对象字面量

    // 通过对象字面量创建对象
    var human = {
        breathe() {
            console.log('breathing...');
        }
    };

    var worker = {
        __proto__: human, //设置此对象的原型为human,相当于继承human
        company: 'freelancer',
        work() {
            console.log('working...');
        }
    };

    human.breathe();        // 输出 ‘breathing...’

    // 调用继承来的breathe方法
    worker.breathe();        // 输出 ‘breathing...’


九、遍历属性的方法

    1、for...in 循环

    2、Object.keys(obj): 返回obj对象的所有key, 返回是一个数组

字符串对象扩展

1、repeat(): 复制字符串 

    var str = '哈哈哈!'.repeat(3);        // str输出 '哈哈哈!哈哈哈!哈哈哈!'

2、includes()、startsWith()、endsWith(): 查找三个方法

    var str = 'siguang 1983';
    str.includes('o');        // 查找字符串中是否包含值,包含返回true否则返回false
    str.startsWith('s');    // 第一个字符是不是s,如果是返回true
    str.endsWith('d');        // 最后一个字符是不是 d,返回布尔值

3、padStart(): 补全长度从开头, 'x'.padStart(4, 'ab') // 'abax'

4、padEnd(): 补全长度从结尾, 'x'.padEnd(4, 'ab') // 'xaba'

Number、Math对象扩展

一、Math扩展

    1、trunc(): 去除小数部分 Math.trunc(123.123123);    // 123

    2、sign(): 判断是正数还是负数,如果是正数返回1,负数返回-1,0返回0

    3、Math.sign(-234);    // -1

    4、hypot(): 返回所有参数的平方和的平方根,Math.hypot(3,4);    // 5 勾股定理

二、Number对象扩展

    1、isFinite(): 是否是无穷数

    2、Number.isNaN(): 是否是NaN

    3、parseInt('12.34'): es5的写法   Number.parseInt('12.34'): es6的写法,为了减少全局方法

    4、Number.isInteger(num): 判断是否是一个整数

数组扩展 Array

一、form(): 将对象转成数组

    var obj = {'0': 'a', '1': 'b', '2': 'c'};
    var arr =  Array.form(obj); console.log(arr);        // [a, b, c]


二、of(): 将一组值转成数组

    var arrA = new Array(1, 2, 3);    // 生成一个[1,2,3]
    var arrB = new Array(3);                // arrB.length  等于3 如果传一个参数这样会生成一个3个空值
    var arrC = Array.of(3);                    // [3] 解决了new Array的问题


三、find(): 找出第一个符合条件的数据元素

    // 找出数组中值大于3的第一个数
    var arr = [1,2,3,4,5,6];
    var n = arr.find(function(value, index){
        return value > 3;
    })
    console.log(n);        // 4


四、findIndex(): 找出第一个符合条件的位置,也就是所引值

五、fill(填充内容, 填充开始的位置, 填充结束的位置): 填充数组

    var arr = [1,2,3,4,5,6];
    arr.fill(7);        // 数组里全部为7
    arr.fill(7, 1, 3);    // 数组下标从1-3的值为7
    console.log(arr);


六、for...of: 遍历数组、字符串的值,但不能遍历对象

    // var arr = 'sdfsfdasdfasdf';
    var arr = [1,2,3,4,5,6];
    for(var value of arr){
        console.log(value);        // 1,2,3,4,5,6 只能将遍历出数组的值
    }


七、keys()和values(): 返回数组的Key或value

    // var arr = 'sdfsfdasdfasdf';
    var arr = [1,2,3,4,5,6];
    for(var value of arr.key()){
        console.log(value);
    }


八、entries(): 将数组的key和value一块输出

    var arr = ['a', 'b', 'c', 'd', 'e', 'f'];
    for(var [key, value] of arr.entries()){
        console.log(key, value);
    }

    返回: 
        0 "a"
        1 "b"
        2 "c"
        3 "d"
        4 "e"
        5 "f"


九、includes(): 查看数组中是否包含指定的值

    var arr = [1, 2, 3, 4, 5];
    arr.includes(3);        // true

二进制数组 ArrayBuffer对象

二进制数组由三类对象组成: 

    ArrayBuffer对象: 代表原始的二进制数据。

    TypedArray视图: 用来读写简单类型的二进制数据。

    DataView视图: 用来读写复杂类型的二进制数据。


一、ArrayBuffer 对象

    它不能直接读写,只能通过(TypedArray和DataView)来读写。

    ArrayBuffer也是一个构造函数,可以分配一段可以存放数据的连续内存区域。

    var buf = new ArrayBuffer(32);        // 生成一段32字节的内存区域,每个字节默认为0


    属性和方法: 

    1、byteLength: 返回分配内存的字节长度

        var buffer = new ArrayBuffer(32);

        buffer.byteLength;        // 32


    2、slice(): 拷贝字节

        var buffer = new ArrayBuffer(8);

        var newBuffer = buffer.slice(0, 3);     //拷贝`buffer`对象的前三个字节(从0到3前结束),生成一个新的ArrayBuffer对象: newBuffer


    3、isView(): 返回一个布尔值,表示参数是否为ArrayBuffer的视图实例

        var buffer = new ArrayBuffer(8);
        ArrayBuffer.isView(buffer) // false

        var v = new Int32Array(buffer);
        ArrayBuffer.isView(v) // true


二、TypedArray 视图

    TypedArray数组只提供9种固定的构造函数

Symbol

JS新的数据类型,独一无二的值,凡是属性名属于Symbol类型,就是独一无二的

# 没有参数的情况
var s1 = Symbol();
var s2 = Symbol();

s1 === s2         // false

# 有参数的情况
var s1 = Symbol('foo');
var s2 = Symbol('foo');

s1 === s2         // false

一、不能与其它值进行运算

    var sym = Symbol('My symbol');
    console.log("your symbol is " + sym);        // 报错


二、toString()转成字符串

    let sym = Symbol('my symbol');
    sym.toString()    // "Symbol(my symbol)"

Proxy对象拦截器

可以监听对象身上发生变化,对操作对象属性读取时做一个拦截器

<script>
    let obj = {
        a: 1,
        b: 2
    }

    // 相当于操作对象属性时的一个拦截器
    let oProxy = new Proxy(obj, {
        get(obj, key) {
            return obj[key];
        },
        set(obj, key, value) {
            // 这里来做拦截,如果修改的key=a 并且 value值小于10不会就将值修改
            if (key == 'a' && value < 10) {
                obj[key] = value;
            }
        }
    })

    // 设置属性
    oProxy.a = 9; // {a: 9, b: 2}
    oProxy.a = 20; // {a: 9, b: 2}
    oProxy.b = 10; // {a: 9, b: 20}

    // 获取属性值
    console.log(oProxy.a, oProxy.b);    
</script>

Promise异步

// resolve: 成功、reject: 失败
var pro = new Promise(function(resolve, reject){        // resolve成功, reject失败
    // 将一个耗时长的任务放到执行器里
    setTimeout(function(){
        resolve();            // 4毫秒后执行成功
    }, 400)
})

pro.then(
    function(){                        // reslove回调
        console.log('成功执行');
    },
    function(){                        // reject回调
        console.log('失败执行');
    }
)
.catch(function(e){            // 捕获异常,如果then中的两个方法成功失败有报错就会走catch
    console.log(e)
})

// 如果不需要reslove的回调可以设置为null
por.then(null, functino(){
    console.log('失败执行')
})


方法: 
一、Promise.all([实例1,实例2,实例3]): 用于多个promise实例,当三个实例都为真的时候all这个结果为真

    var p1 = new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve();
            console.log("p1完成");
        }, 400);
    })

    var p2 = new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve();
            console.log("p2完成");
        }, 1000);
    })

    var p3 = new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve();
            console.log("p3完成");
        }, 4000);
    })

    var p4 = Promise.all([p1, p2, p3]);
    p4.then(function(){
        console.log('三个全部执行成功')
    }, function(){
        console.log('失败')
    })

二、Promise.race([实例1,实例2,实例3]): 只要有一个为成功,p4为成功,与all()方法正反

https://www.jianshu.com/p/c98eb98bd00c

Generator

Generator是一个状态机,封装了多个内部状态, 也是一个遍历对象生成函数,返回遍历器对象

Example: 

    function* ouputGenerator() {
        yield 'hello';            // 调用后不会马上执行,只有调用next()才会返回
        yield 'world';
        return 'ending';
    }
    let gen = ouputGenerator();

    console.log(gen.next());     // {value: "hello", done: false}  value就是返回值
    console.log(gen.next());     // {value: "world", done: false}
    console.log(gen.next());     // {value: "ending", done: true}  done为true就是后面没有yeild的定义


一、Generator的特征:

    1、function 关键字与函数名之间有一个"*"号

    2、函数体内有 yield 语句

    3、可以执行暂停

    4、generator也可以不用yield, 但需要执行一次next()


二、属性和方法

    1、next() 可以带一个参数,参数可以是上一个yield的返回值

        done为true 表示结束  Object {value: "ending", done: true}

    2、value: 就是next返回的值,done为false表示已经结束

    3、yield: 定义表达式


三、Generator与Promise的区别

    promise如果写多个嵌套会很麻烦:

        getArticleList()
            .then(articles => getArticle(articles[0].id))
            .then(article => getAuthor(article.authorId))
            .then(author => {
                alert(author.email);
            });

        function getAuthor(id){
            return new Promise(function(resolve, reject){
                $.ajax("http://beta.json-generator.com/api/json/get/E105pDLh",{
                    author: id
                }).done(function(result){
                    resolve(result);
                })
            });
        }

        function getArticle(id){
            return new Promise(function(resolve, reject){
                $.ajax("http://beta.json-generator.com/api/json/get/EkI02vUn",{
                    id: id
                }).done(function(result){
                    resolve(result);
                })
            });
        }

        function getArticleList(){
            return new Promise(function(resolve, reject){
            $.ajax(
                "http://beta.json-generator.com/api/json/get/Ey8JqwIh")
                .done(function(result){
                    resolve(result);
                }); 
            });
        }


    Generator的写法:

        function* run(){
            var articles = yield getArticleList();
            var article = yield getArticle(articles[0].id);
            var author = yield getAuthor(article.authorId);
            console.log(author.email);  
        }

        var gen = run();
        gen.next().value
            .then(articles => {
                gen.next(articles).value.then(article => {
                    gen.next(article).value.then(author => {
                    gen.next(author)
                })
            })
        })

async、await

async 是ES7引入的函数,使的异步变得更加方便

await: 命令后面是一个Promise对象

Generator函数执行必须靠执行器,也就是需要调next(),async函数有自带的执行器,async返回的是Promise对象

一、async的多种声明

    // 函数声明
    async function foo() {}

    // 函数表达式
    const foo = async function () {};

    // 对象的方法
    let obj = { async foo() {} };
    obj.foo().then(...)

    // Class 的方法
    class Storage {
        constructor() {
            this.cachePromise = caches.open('avatars');
        }

        async getAvatar(name) {
            const cache = await this.cachePromise;
            return cache.match(`/avatars/${name}.jpg`);
        }
    }

    // 箭头函数
    const foo = async () => {};

二、async函数返回一个Promise对象,函数内部有return返回值,可以通过then方法来接收

    async function f(){
        return 'hello world'
    }

    f().then( v => {
        console.log(v);
    })

Example:

    async function getStockPriceByName(name) {
        var symbol = await getStockSymbol(name);
        var stockPrice = await getStockPrice(symbol);
        return stockPrice;
    }

    getStockPriceByName('goog').then(function (result) {
        console.log(result);
    });

| http://www.ruanyifeng.com/blog/2016/01/babel.html
| http://es6.ruanyifeng.com/#docs/style
| http://babeljs.cn/