ES6二次学习

作者: ccminn | 来源:发表于2017-08-12 18:22 被阅读53次

    前言

    笔记仅记录了相比于第一次学习,重新理解的知识点
    完整内容详见:《ECMAScript 6 入门》 阮一峰

    总结

    经历两个星期结合ES6的React实践,决定再回头看一遍阮一峰的ES6教程,发现收获多多,常看常新。
    第一遍看时,比较敷衍,没有实践自然也理解不深,综合来说,阮一峰的教程更适合查漏补缺,而不是作为入门教材。

    索引

    1. 解构赋值的用途
    2. 尾调用优化
    3. 扩展运算符的用途
    4. 数组的方法扩展
    5. 对象
    6. Set类型 : distinct Array
    7. Map类型:可扩展键名,值—值对
    8. Proxy对象
    9. Promise对象
    10. Iterator 迭代器
    11. Generator生成器
    12. async函数
    13. Class 类

    1. 解构赋值的用途

    ① 交换变量值: [x, y] = [y, x]
    ② 函数返回值的获取更便利: let [a, b, c ] = func()
    ③ 函数无序传参
    有序传参:function func([x,y,z]) { } func([1,2,3])
    无序传参:function func({x, y, z}) { } func({z: 3, y: 2 , x: 1})
    ④ 提取JSON数据更方便
    ⑤ 函数传参可设置默认值,如预设Ajax请求的参数
    当传入Ajax请求的参数不声明async与cache值,默认都为true
    jQuery.ajax = function (url, { async = true, cache = true}) {};
    ⑥ Map类型取键名/值 : for ( let [key, value] of map) { }
    ⑦ 加载CommonJS模块中的指定方法: import { func1 , func2 } from '../common.js'

    2. 尾调用优化

    在函数的最后一步(即return)中调用另一个函数,形如return g(a,b);,叫做尾调用。
    存储于内存中的函数调用记录(包含了调用内存位置、变量等信息),叫做调用帧,嵌套函数的调用帧形成了调用栈;这很浪费内存。
    因此在return句中采用尾调用,当内层函数的调用不再用到外层函数中的信息,就可用最新内层函数的调用帧取代外层函数的调用帧,节省内存。

    3. 扩展运算符的用途

    ① 取代concat()合并数组: let arr = [...arr1, ...arr2];
    ② 与解构赋值并用,截取部分数组: let [item, ...rest] = array;
    ③ 分解对象类型的函数返回值: var { data, code, msg } = getData();
    ④ 字符串转数组: [...'hello] 等同于 'hello'.split('');
    ⑤ 正确识别32位的Unicode字符串: [ ...str ].length 获取unicode字符串正确长度

    4. 数组的方法扩展

    array.find(function) :查找第一个符合条件的数组元素
    array.findIndex() :与indexOf功能相同,但findIndex优于可查找NaN元素
    new Array(5).fill(0) :创建数组并且初始化为全0
    ④ 数组元素遍历
    array.keys() :遍历键名
    array.values() :遍历键值
    array.entries() :遍历键值对
    array.includes(value): 数组查找,可查找NaN

    5. 对象

    let a = { [key]: value} :用表达式作键名
    Object.is(value1, value2 ): 严格一致,不会自动转换类型,准确性高于“===”
    Object.assign(target, obj1, obj2 ): 合并对象属性到第一个参数中;是浅拷贝(符合类型为引用)
    object.__proto__属性指向Object.prototype属性
    object.__proto__属性推荐使用的三个操作方法:
    Object.setPrototypeOf(object, prototype)
    Object.getPrototypeOf(obj);
    Object.create()
    prototype__proto__属性
    Array.prototype:Array原型的所有方法
    new Array(3).__proto__ : 原型实例的自有属性方法,内包含了Array.prototype中的所有方法
    ⑥ 对象的扩展:let { x, y, ...z } = { x: 1, y: 2, a: 3 , b: 4} ; 则 z = { a: 3 , b: 4 }

    6. Set类型 : distinct Array

    .add()
    .size
    .delete()
    .has(value)
    .clear()
    ⑥ Set转数组: Array.from(set)
    数组去重: Array.from (new Set(array))

    7. Map类型:可扩展键名,值—值对

    .size
    .set(key, value)
    .get(key)
    .has(key)
    .delete(key)
    .clear()
    ⑦ 遍历的顺序即插入的顺序
    ⑧ Map转数组: ...map

    8. Proxy对象

    进程与对象实体之间的拦截层

    9. Reflect对象

    用于部署对象的新方法;可以通过Reflect对象拿到语言内部的方法,如Object.defineProperty

    10. Promise对象

    ① 封装了一个函数
    ② 对内部函数返回的resolvereject进行分发处理(.then.catch
    ③ Promise新建后,立即执行

    11. Iterator 迭代器

    Iterator.next()的返回值: {value: 'value', done: false}{ value: undefined, done: true}
    ② 悄悄调用了可遍历数据的Iterator的情况:
    解构赋值有序对象(数组、set)、
    扩展运算符...
    yield* 表达式: yield* [2,3,4] 等同于 yield 2; yield 3; yield 4;
    let ... in :遍历键名
    let ... of:遍历键值(适用于有Iterator接口的数据)

    12. Generator生成器

    ① 相当于一个封装了多个状态的状态机
    ② 作用:生成并且返回一个遍历器对象
    ③ 调用后并不立即执行,而是返回一个指向内部状态的指针对象(遍历器对象)
    ④ 调用iterator.next(),才是真正执行内部函数的时机
    yield可以嵌入普通语句中,但要放在圆括号中: console.log( 'hello' + (yield 123) );
    ⑥ yield表达式本身的返回值是undefined,但可以用next(value)对上一个yield表达式赋值
    yield * generator():在一个生成器函数中调用另一个生成器函数
    ⑧ 封装异步函数,实现回调

    function* demo () {
        // do something  Part1 
        yield go();           // 相当于分离了异步的两个阶段,part1与part2
        // do something  Part2
    }
    
    let g = demo();
    g.next();      // 执行part1
    g.next();      // part1执行完之后,再执行part2
    

    ⑨ 重点理解:
    Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。
    在第一次调用.next()时,执行到yield语句(yield语句本身也被执行)。等下一次next调用,执行下一段包含yield语句的代码段。

    function* demo () {
        do thing 1     
        yield y1;
    <---第一次执行next,执行到此处---->
        do thing 2
        yield y2;
    <---第二次执行next,执行到此处---->
        do thing 3 
        yield y3;
    <---第三次执行next,执行到此处---->
        do thing 4 
        yield y4;
    <---第四次执行next,执行到此处---->
    }
    
    // 代码实例
      var a = 0;
    
      function* demo() {
        console.log('in demo  a = 3')
        console.log(yield a=3);
        console.log('in demo  a = 4')
        console.log(yield a=4);
        console.log('in demo  a = 5')
        console.log(yield a=5);
      }
    
      let g = demo();
      console.log('首次next');
      console.log(a);
      console.log( g.next());
      console.log(a);
      console.log('二次next')
      console.log(a);
      console.log( g.next())
      console.log(a);
      console.log('三次next')
      console.log(a);
      console.log( g.next())
      console.log(a);
    
    yield实例运行的console结果

    13. async函数

    是generator的语法糖,简化了异步操作。相比generator,async具有以下优点:
    ① 内置执行器,单行调用即可完成generator函数的所有.next()
    ② 更具有语义性
    ③ 适用性更广
    yield命令:只能后跟 Thunk 或Promise
    await命令:可后跟Promise对象与原始值
    ④ async函数的返回值是Promise对象
    async将generator返回的Iterator对象自动执行后,并将结果封装为一个Promise对象,便于后期处理。
    return await 123; ==> 转为resolve状态的Promise对象
    async中return语句返回值,传入.then()作回调参数。
    return Promise.reject('出错了'); ==> 转为reject状态的Promise对象
    async中抛出错误,返回reject状态的Promise对象,传入.catch()方法
    ⑤ forEach中的async是并发操作的,应该改用for循环

    // 不推荐用forEach写法
    function dbFuc(db) { //这里不需要 async
      let docs = [{}, {}, {}];
    
      // 可能得到错误结果
      docs.forEach(async function (doc) {
        await db.post(doc);
      });
    }
    
    // 推荐用for循环
    async function dbFuc(db) {
      let docs = [{}, {}, {}];
    
      for (let doc of docs) {
        await db.post(doc);
      }
    }
    

    14. Class 类

    ① super
    super(): 代表父类的构造函数传入父类的this,等同于Class.prototype.constructor.call(this)
    super:指向父类的原型对象,等同于Class.prototype,可用于调用父类的静态方法
    prototype__proto__
    子类的__proto__ 指向父类,表示构造函数的继承
    子类的prototype.__proto__指向父类的prototype属性, 表示方法的继承
    子类实例的__proto__.__proto__指向父类实例的__proto__属性

    相关文章

      网友评论

      本文标题:ES6二次学习

      本文链接:https://www.haomeiwen.com/subject/evkjrxtx.html