ES6重点-0

2020-02-18 23:43:0510/1/2021, 3:34:43 AM

Proxy

Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta program),即对编程语言进行编程。Proxy可以理解成在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

Reflect

**WHAT **😳

ES6为了操作对象而提供的新的API

WHY 🤔

  1. Object对象的一些明显是语语言内部的方法(比如Object.defineProperty)放到Reflect对象上,现阶段,某些方法同时在ObjectReflect对象上部署,未来的新方法只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法
  2. 修改某些Object方法的返回结果,让其变得更合理。比如Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false
  3. Object操作都变成函数行为。某些Object操作是命令式,比如name in objdelete obj[name],而Reflect.has(obj, name)Reflect.deleteProperty(obj, name)让它们变成了函数行为
  4. Reflect对象的方法与Proxy对象的方法一一对应,这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为

HOW 😮

Reflect对象一共有13个静态方法

// 适用于函数定义了自己的 apply 方法
Reflect.apply(target, thisArg, args)
// 等同于new target(...args) 这提供了一种不使用 new 来调用构造函数的方法
Reflect.construct(target, args)

Reflect.get(target, name, receiver)

Reflect.set(target, name, value, receiver)

Reflect.defineProperty(target, name, desc)

Reflect.deleteProperty(target, name)

Reflect.has(target, name)
// 基本等同于Object.getOwnPropertyNames 与 Object.getOwnPropertySymbols 之和
Reflect.ownKeys(target)

Reflect.isExtensible(target)

Reflect.prevenExtensions(target)

Reflect.getOwnPropertyDescriptor(target, name)

Reflect.getPrototypeOf(target)

Reflect.setPrototypeOf(target, prototype)

Reflect + Proxy实现观察者模式

const observers = new Set()
function observe(fn) {
  observers.add(fn)
}
function observable(obj) {
  return new Proxy(obj, {
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver)
      observers.forEach(observer => observer())
      return result
    }
  })
}

const person = observable({
  name: "tom",
  age: 23
})
function print() {
  console.log(`${ person.name }, ${ person.age }`)
}
observe(print)
person.name = "tom1" 

📣 在观察者模式中,观察者是知道被观察者的,被观察者一一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在,它们只有通过消息代理进行通信。在发布订阅模式中,组件时松散耦合的,正和观察者模式相反。观察者模式大多时候是同步的,比如当事件触发,被观察者就会调用观察者的方法;而发布订阅模式大多时候是异步的(使用消息队列)

Symbol

WHAT 😳

ES6引入的一种新的原始数据类型,表示独一无二的值。它是JavaScript语言的第七种数据类型,前六种是:undefinednullBooleanStringObjectNumber

📣 不支持 new Symbol()

WHY 🤔

ES5的对象属性名都是字符串,这容易造成属性名的冲突。Symbol值可作为对象属性名

HOW 😮

Symbol值通过Symbol函数生成

const nameProperty = Symbol("name")
let a = {
  [nameProperty]: "a",
  age: 21
}

console.log(Object.getOwnPropertyNames(a))
for (const key in a) {
  if (a.hasOwnProperty(key)) {
    const element = a[key];
    console.log(key);
  }
}
console.log(Reflect.ownKeys(a));

API:

  • Symbol.description

    创建Symbol的时候,可以添加一个描述,但读取这个描述需要将Symbol显式转为字符串。Symbol.description可以方便的读取Symbol对应的描述

    const nameProp = Symbol("name")
    nameProp.toString()
    console.log(nameProp.description)
    
  • Symbol.for

    重新使用同一个Symbol值。Symbol.for接受一个字符串作为参数,然后全局搜索有没有以该参数作为名称的Symbol值,若有,返回之;若没有,创建之

    Symbol.for("name")Symbol("name")的区别:两者都会生成Symbol值;前者会被登记在全局环境中供搜索,后者不会

    Symbol("name") === Symbol("name")
    // false
    Symbol.for("name") === Symbol.for("name")
    // true
    
  • Symbol.keyFor

    返回一个已登记Symbolkey

    console.log(Symbol.keyFor(Symbol("name")));
    console.log(Symbol.keyFor(Symbol.for("name")));
    // undefined
    // name
    

Set

WHAT 😳

ES6提供的一种数据结构,类似于数组,但成员的值都是唯一的,没有重复的值

Set使用一种类似于===的算法“Same-value-zero equality”来判断值是否相等

// "5" 和 5 不相等
// NaN 和 NaN 相等
// 两个空对象不相等
let set = new Set()
set.add("5")
set.add(5)
set.add(NaN)
set.add(NaN)
set.add({})
set.add({})
console.log(set);
// Set { '5', 5, NaN, {}, {} }

WHY 🤔

  • 可以用作数组去重[...new Set([1, 1, 2, 2, 3])]
  • 去除字符串里面的重复字符[...new Set('aaabbbc')].join("")

HOW 😮

Set函数可以接受一个数组(或具有interable接口的其他数据结构)作为参数,用来初始化

Map

WHAT 😳

ES6提供的一种数据结构,类似于对象,也是键-值对的集合,但键的范围不限于字符串,各章类型的值(包括对象)都可以作为键

WHY 🤔

传统上的Object只能用字符串([Symbol](## Symbol))作为键,这给它的使用带来了很大的限制

Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性和原作者的属性同名

HOW 😮

使用 new Map 来创建Map

const map = new Map([[2, "2"]])
map.set(1, "1")
console.log(map.get(2));

事实上,不仅仅是数组,任何具有iterator接口、且每个成员都是一个双元素的数组的数据结构都可以作为Map构造函数的参数