侯策《前端开发核心知识进阶》读书笔记——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

posted @ 2020-05-02 00:26  姚啊姚  阅读(423)  评论(0编辑  收藏  举报