• 侯策《前端开发核心知识进阶》读书笔记——ES


    数组API——includes

    Array.prototype.includes(value : any): boolean
    [1, 2, 3].includes(3) // true

    这是判断数组中是否含有一个元素的方法,该方法最终返回一个布尔值。

    现成的判断数组中是否含有一个元素的方法:

    [1, 2, 3].findIndex(i => i === 2) 
    // 1
    
    [1, 2, 3].find(i => i == 2) 
    // 2
    
    [1, 2, 3].indexOf(2) 
    // 1

    实现一个一模一样的api

    const includes = (array, target) =>  !!~ array.indexOf(target)
    
    includes([1,2,3], 3)
    // true
    
    includes([1,2,3], 4)
    // false

    新特性的意义:首先,在语义上它直观明朗,这是 indexof 所无法取代的。当然还有更深层次的必要性和不可替代性。

    Array.prototype.indexOf 采用的是 === 比较,而Array.prototype.includes 不同,它采用了 SameValueZero() 比较。

    SameValueZero() 是引擎内置的比较方式,并没有对外接口,其实现采用了 Map 和 Set。采用这种比较,最直接的收益就是可以判断 NaN:

    [NaN].includes(NaN) // true
    [NaN].indexOf(NaN) // -1
    
    NaN === NaN
    // false

    Object Spread VS Object.assign

    Object Spread 和 Object.assign 在很多情况下做的事情是一致的,它们都属于 ES Next 的新特性,当然 Object Spread 更新。事实上,规范说明中,也告诉我们 “object spread”:{… obj} 和 Object.assign({},obj) 是等价的。

    但是一定还具有区别。实际上,Object.assign() 将会修改它的第一个参数对象,这个修改可以触发其第一个参数对象的 setter。从这个层面上讲,Object spread 操作符会创建一个对象副本,而不会修改任何值,这也许是更好的选择,也更加符合React/Redux的“不可变性”的概念。

    如果使用 Object.assign(),我们始终保证一个空对象作为第一个参数,也能实现同样的“不可变性”,但是性能比 Object Spread 就差的比较多了。

    箭头函数

    首先了解箭头函数的概念:箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj

    var obj = {
        birth: 1990,
        getAge: function () {
            var b = this.birth; // 1990
            var fn = function () {
                return new Date().getFullYear() - this.birth; // this指向window或undefined
            };
            return fn();
        }
    };
    
    var obj = {
        birth: 1990,
        getAge: function () {
            var b = this.birth; // 1990
            var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
            return fn();
        }
    };
    obj.getAge(); // 25

    哪些场景下不适合使用 ES6 箭头函数?

    • 构造函数的原型方法上

    构造函数的原型方法需要通过 this 获得实例,因此箭头函数不可以出现在构造函数的原型方法上:

    Person.prototype = () => {
      // ...
    }
    • 需要获得 arguments 时

    箭头函数不具有 arguments,因此在其函数体内无法访问这一特殊的伪数组,那么相关场景下也不适合使用箭头函数。

    使用对象方法时

    const person = {
      name: 'lucas',
      getName: () => {
        console.log(this.name)
      }
    };
    person.getName()

    getName 函数体内的 this 指向 window,显然不符合其用意

    • 使用动态回调时
    const btn = document.getElementById('btn')
    
    btn.addEventListener('click', () => {
        console.log(this === window)
    });

    当点击 id 为 btn 的按钮时,将会输出:true,事件绑定函数的 this 指向了 window,而无法获取事件对象。

    Proxy 代理

    1、new的处理

    class Person {
      constructor (name) {
           this.name = name
      }
    }
    
    let proxyPersonClass = new Proxy(Person, {
      apply (target, context, args) {
        throw new Error(`hello: Function ${target.name} cannot be invoked without 'new'`)
      }
    })

    对 Person 构造函数进行了代理,这样就可以防止非构造函数实例化的调用

    proxyPersonClass('lucas')
    
    // VM173058:9 Uncaught Error: hello: Function Person cannot be invoked without 'new'
        at <anonymous>:1:1
    
    new proxyPersonClass('lucas')
    // {name: "lucas"}

    也可以静默处理非构造函数实例化的调用,将其强制转换为 new 调用

    class Person {
      constructor (name) {
           this.name = name
      }
    }
    
    let proxyPersonClass = new Proxy(Person, {
      apply (target, context, args) {
        return new (target.bind(context, ...args))()
      }
    })
    
    proxyPersonClass('lucas')
    // Person {name: "lucas"}

    2、 assert 处理

    const lucas = {
        age: 23
    }
    assert['lucas is older than 22!!!'] = 22 > lucas.age
    
    // Error: lucas is older than 22!!!
    //我们看 assert 赋值语句右侧表达式结果为一个布尔值,当表达式成立时,断言不会抛出;如果 assert 赋值语句右侧表达式不成立时,也就是断言失败时,断言抛出错误。

    assert实现,本质还是拦截操作:

    const assert = new Proxy({}, {
      set (target, warning, value) {
        if (!value) {
            console.error(warning)
        }
      }
    })

    Decorator (待完善)

    装饰器(Decorators)让你可以在设计时对类和类的属性进行“注解”和修改。

    Babel 

    编译 ES Next 代码,进行降级处理,进而规避了兼容性问题。

    Babel 的核心原理是使用 AST(抽象语法树)将源码进行分析并转为目标代码。

    const、let 编译

    简单来说,const、let 一律转成 var。为了保证 const 的不可变性:Babel 如果在编译过程中发现对 const 声明的变量进行了二次赋值,将会直接报错,这样就在编译阶段进行了处理。至于 let 的块级概念,ES5 中,我们一般通过 IIFE 实现块级作用域,但是 Babel 处理非常取巧,那就是在块内给变量换一个名字,块外自然就无法访问到。

    var foo = 123
    
    {
      foo = 'abc'
      let foo
    }
    
    //Uncaught ReferenceError: Cannot access 'foo' before initialization

     Babel 编译会将 let、const 变量重新命名,同时在 JavaScript 严格模式(strict mode)不允许使用未声明的变量,这样在声明前使用这个变量,也会报错。

    "use strict";
    var foo = 123
    {
      _foo = 'abc'
      var _foo
    }

    const 声明的变量一旦声明,其变量(内存地址)是不可改变 :

    const foo = 0
    foo = 1
    
    // VM982:2 Uncaught TypeError: Assignment to constant variable
    
    "use strict"; function _readOnlyError(name) { throw new Error(""" + name + "" is read-only"); } var foo = 0; foo = (_readOnlyError("a"), 1);

    Babel 检测到 const 声明的变量被改变赋值,就会主动插入了一个 _readOnlyError 函数,并执行此函数。这个函数的执行内容就是报错,因此代码执行时就会直接抛出异常。

    for 循环问题

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

    为了保存每一个循环变量 i 的值,Babel 也使用了闭包

    "use strict";
    var array = [];
    
    var _loop = function _loop(i) {
      array[i] = function () {
        console.log(i);
      };
    };
    
    for (var i = 0; i < 10; i++) {
      _loop(i);
    }
    array[6]();

    箭头函数的编译分析

    var obj = {
        prop: 1,
        func: function() {
            var _this = this;
    
            var innerFunc = () => {
                this.prop = 1;
            };
    
            var innerFunc1 = function() {
                this.prop = 1;
            };
        },
    
    };
    
    
    var obj = {
        prop: 1,
        func: function func() {
            var _this2 = this;
    
            var _this = this;
    
            var innerFunc = function innerFunc() {
                _this2.prop = 1;
            };
    
            var innerFunc1 = function innerFunc1() {
                this.prop = 1;
            };
        }
    
    };

    通过 var _this2 = this; 保存当前环境的 this 为 _this2,在调用 innerFunc 时,用新储存的 _this2 进行替换函数体内的 this 即可。

    Decorators 的编译(待完善)

    ES6 尾递归调用问题(待完善)

    参考资料:https://www.cnblogs.com/liyuanhong/articles/10139214.html

  • 相关阅读:
    jq focus 在火狐(Firefox)下无效
    将自己的项目上传到github保管
    $(window).height(),在火狐下面获取的高度并不是可视区域的高度
    [转载]跨域iframe高度自适应
    用css改变默认的checkbox样式
    js,jq新增元素 ,on绑定事件无效
    xshell配色Solarized Dark
    记一次给公司服务器装第二块硬盘的经历
    【shell编程基础3】shell编程的组合应用之二:管道及其命令
    【shell编程基础2】shell组合应用之一:重定向和逻辑
  • 原文地址:https://www.cnblogs.com/fmyao/p/12814832.html
Copyright © 2020-2023  润新知