初识Proxy,Reflect,WeakMap
Vue3源码reactive的核心实现用到了Proxy、Reflect、WeakMap等新语法,所以理解其响应式原理的前提是先熟悉这些新语法。
# 1.Proxy
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
var obj = new Proxy({}, {
get: function (target, propKey, receiver) {
console.log(`getting ${propKey}!`);
return Reflect.get(target, propKey, receiver);
},
set: function (target, propKey, value, receiver) {
console.log(`setting ${propKey}!`);
return Reflect.set(target, propKey, value, receiver);
}
});
obj.count = 1
// setting count!
++obj.count
// getting count!
// setting count!
// 2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
上面代码对一个空对象架设了一层拦截,重定义了属性的读取(get)和设置(set)行为。receiver 是指的Proxy的实例。Reflect 是反射对象。
var proxy = new Proxy(target, handler);
Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
target 可以接收对象,函数
handler 接收的是对象如果不传或者传空对象,那么Proxy的实例相当于未被代理
提示
new Proxy(target, {...})是浅代理,也就是说只会代理target这个对象的第一层,如果要代理对象的每一层,做递归处理即可。
# 2.Reflect
Reflect 意为反射,是ES6为了操作对象设计的API,一般配合Proxy一起使用,它与Proxy内部的api完全一致。设计这个API的目的有以下几个
- 将
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对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。
Reflect对象一共有 13 个静态方法。
- Reflect.apply(target, thisArg, args)
- 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)
- Reflect.ownKeys(target)
- Reflect.isExtensible(target)
- Reflect.preventExtensions(target)
- Reflect.getOwnPropertyDescriptor(target, name)
- Reflect.getPrototypeOf(target)
- Reflect.setPrototypeOf(target, prototype)
上面这些方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。
# 3. WeakMap
# 3.1WeakMap的特点
WeakMap只接受对象作为键名WeakMap的键名所引用的对象是弱引用 这个弱引用的特性,就是WeakMaps保持了对键名所引用的对象的弱引用,即垃圾回收机制不将该引用考虑在内。只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。
也正是因为这样的特性,WeakMap 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakMap 不可遍历。
# 3.2WeakMap和Map的区别
下面的这个例子很好的演示了他们之间的区别
const map = new Map();
const weakmap = new WeakMap();
(function(){
const foo = {a: 1};
const bar = {b: 2};
map.set(foo,1)
weakmap.set(bar,2)
})()
console.log(map)
console.log(weakmap)
2
3
4
5
6
7
8
9
10
11
立即执行函数(IIFE)执行完毕之后,由于bar是弱引用,它将会被垃圾回收器回收,而foo不会回收,它会一直保留在map中,如下图所示:
