zoukankan      html  css  js  c++  java
  • 侯策《前端开发核心知识进阶》读书笔记——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

  • 相关阅读:
    poj 3666 Making the Grade
    poj 3186 Treats for the Cows (区间dp)
    hdu 1074 Doing Homework(状压)
    CodeForces 489C Given Length and Sum of Digits...
    CodeForces 163A Substring and Subsequence
    CodeForces 366C Dima and Salad
    CodeForces 180C Letter
    CodeForces
    hdu 2859 Phalanx
    socket接收大数据流
  • 原文地址:https://www.cnblogs.com/fmyao/p/12814832.html
Copyright © 2011-2022 走看看