前言
众所周知,vue2.x版本实现双向绑定是利用的Object.defineproperty实现的,它有不少的缺点,例如无法检测到对象属性的新增或删除,无非监听数组变化等。所以在vue3版本,对其进行了改进,利用es6的新语法proxy改写了响应式原理。
那么你知道proxy到底是什么吗?另外Reflect你知道吗,它也是es6对操作对象设计的API。一起来看看吧
另外,本期博客参与了【新星计划】,还请大家三连支持一下🌟🌟🌟感谢感谢💓💓💓
目录
Proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
可以看到,mdn对proxy的解释是创建一个对象的代理,不难想到,vue3的响应式数据并不会污染源数据。
它用于修改某些操作的默认行为,即对编程语言层面进行修改,属于“元编程”,Proxy意思为“代理”,即在访问对象之前建立一道“拦截”,任何访问该对象的操作之前都会通过这道“拦截”,即执行Proxy里面定义的方法。
语法
const p = new Proxy(target, handler)
参数解释:
target
要使用 Proxy
包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler
一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p
的行为。
Proxy一共支持13种拦截操作(不是Proxy自身的函数,是handler对象支持的方法):
- apply(target, object, args)
- construct(target, args)
- get(target, propKey, receiver)
- set(target, propKey, value, receiver)
- defineProperty(target, propKey, propDesc)
- deleteProperty(target, propKey)
- has(target, propKey)
- ownKeys(target)
- isExtensible(target)
- preventExtensions(target)
- getOwnPropertyDescriptor(target, propKey)
- getPrototypeOf(target)
- setPrototypeOf(target, proto)
具体可看proxy传送门
案例:实现观察者模式
const observers = new Set();
const observed = fn => observers.add(fn);
const observable = obj => new Proxy(obj, {
set: function(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
observers.forEach(observer => observer());
return result;
}
});
Reflect
Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与上面proxy handlers的13个方法相同。
Reflect
不是一个函数对象,因此它是不可构造的。
与大多数全局对象不同,Reflect
并非一个构造函数,所以不能通过new运算符对其进行调用,或者将Reflect
对象作为一个函数来调用。Reflect
的所有属性和方法都是静态的(就像Math对象)。
案例:检测一个对象是否存在特定属性
const duck = {
name: 'Maurice',
color: 'white',
greeting: function() {
console.log(`Quaaaack! My name is ${this.name}`);
}
}
Reflect.has(duck, 'color');
// true
Reflect.has(duck, 'haircut');
// false
具体可看Reflect传送门
两者比较
相通点
- Proxy和Reflect都是es6为了操作对象而提供的新API
- Proxy和Reflect经常混用,例如上面实现观察者模式
- Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。
不同点
- Proxy是拦截对象的操作并定义拦截时具体执行什么操作;而Reflect其实是执行了对象的操作
- 按我理解,Proxy是改写了对象原有方法;而Reflect像是要抽离原来Object上的方法,它就是是调用Object原有方法的函数,目的是将数据和逻辑代码分离。这样以后可能Obejct就只是数据,想要对其操作要用Reflect。
proxy的用处:
- 实现拦截和监视外部对对象的访问。
- 降低函数和类的复杂度,优雅的写出代理代码。
- 在复杂操作前对操作进行校验或对所需资源进行管理。
proxy的应用场景:
- 抽离校验模块。
- 私有属性。
- 访问日志。
- 预警和拦截。
- 过滤操作。
- 中断代理。
reflect的好处:
- 可以把一些不仅仅是Object含有的工具函数,统一起来,比如Reflect.apply,这是针对Function,而不是针对Object的,这些函数放到Object上显然不合适,放在Reflect上,第一没有什么歧义,第二增加了使用的便利性;
-
修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
-
让Object操作都变成函数行为。某些Object操作是命令式,例如把delete等命令式操作,改为Reflect.delete的函数式操作,可以减少保留字的个数;
比如name in obj和delete obj[name],
而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为
- 把散落的工具函数集中到一个统一的对象上,可以保持其他对象的简洁性