JavaScript:Proxy 代理
Proxy 的基本概念
ES6 引入的 Proxy
用于创建一个对象的代理,从而可以拦截和自定义对象的底层操作。Proxy
提供了一种机制,允许在访问或修改对象时添加自定义行为。
Proxy 的语法
const proxy = new Proxy(target, handler);
target
:需要被代理的目标对象。handler
:一个对象,包含拦截操作的“陷阱”(trap)方法。
常见的陷阱方法
get(对象, 对象的某个属性, receiver)
:拦截对象属性的读取操作。set(对象, 对象的某个属性, 给该对象这个属性赋的新值, receiver)
:拦截对象属性的设置操作。has(target, prop)
:拦截in
操作符。deleteProperty(target, prop)
:拦截delete
操作。apply(target, thisArg, args)
:拦截函数调用。construct(target, args, newTarget)
:拦截new
操作符。
一般主要用的就是 get
和 set
示例代码
拦截属性读取和设置
const target = { name: "Alice", age: 25 };
const handler = {get(target, prop) {console.log(`Reading property: ${prop}`);return target[prop];},set(target, prop, value) {console.log(`Setting property: ${prop} to ${value}`);target[prop] = value;return true; // 表示操作成功}
};
const proxy = new Proxy(target, handler);console.log(proxy.name); // 输出: Reading property: name Alice
proxy.age = 30; // 输出: Setting property: age to 30
Proxy 的用途
- 数据验证:在设置属性时验证值的合法性。
- 日志记录:跟踪对象的访问和修改。
- 性能优化:延迟加载或缓存计算结果。
- 实现私有属性:通过拦截访问隐藏某些属性。
注意事项
Proxy
的陷阱方法是可选的,未定义的陷阱会直接转发到目标对象。Proxy
无法拦截一些底层操作,例如Object.keys
或for...in
循环。- 使用
Proxy
可能会带来一定的性能开销,需谨慎在高性能要求的场景中使用。
defineProperty 和 Proxy
Vue2 使用 Object.defineProperty
来实现数据响应式。这种方式通过劫持对象的属性访问和修改来实现数据监听
const data = { name: 'Vue' };
Object.defineProperty(data, 'name', {get() {console.log('触发 getter');return this._name;},set(newVal) {console.log('触发 setter');this._name = newVal;}
});
Vue2 是针对属性的监听,所以它必须去深度遍历对象里的每个属性,才能进行监听(把每个属性的读取和赋值变成函数)
这样的话缺陷就很明显了,深度遍历会导致效率上的损失,并且无法监听属性的新增和删除
Vue3 进行改进,它是监听整个对象,只要对这个对象进行操作,就会收到通知。由于现在不监听属性了,所以不用去遍历了
const data = { name: 'Vue' };
const proxy = new Proxy(data, {get(target, key) {console.log('触发 getter');return target[key];},set(target, key, value) {console.log('触发 setter');target[key] = value;return true;}
});
在上面代码中,我们后续都是通过操作代理对象 proxy
来改变 data