ES6重点-0
Proxy
Proxy
用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta program),即对编程语言进行编程。Proxy
可以理解成在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
Reflect
**WHAT **😳
ES6为了操作对象而提供的新的API
WHY 🤔
- 将
Object
对象的一些明显是语语言内部的方法(比如Object.defineProperty
)放到Reflect
对象上,现阶段,某些方法同时在Object
和Reflect
对象上部署,未来的新方法只部署在Reflect
对象上。也就是说,从Reflect
对象上可以拿到语言内部的方法 - 修改某些
Object
方法的返回结果,让其变得更合理。比如Object.defineProperty(obj, name, desc)
在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)
则会返回false
- 让
Object
操作都变成函数行为。某些Object
操作是命令式,比如name in obj
和delete obj[name]
,而Reflect.has(obj, name)
和Reflect.deleteProperty(obj, name)
让它们变成了函数行为 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
语言的第七种数据类型,前六种是:undefined
、null
、Boolean
、String
、Object
、Number
📣 不支持 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
返回一个已登记的
Symbol
的key
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
构造函数的参数