Proxy 代理
代理(Proxy)是一种可以拦截并改变底层JavaScript引擎操作的包装器,在新语言中通过它暴露内部运作的对象。
介绍
Proxy
主要用于改变对象的默认访问行为,实际上是在访问对象之前增加一层拦截,在任何对对象的访问行为都会通过这层拦截。在这层拦截中,我们可以增加自定义的行为。
JS
var proxy = new Proxy(target,handler)
var obj = new Proxy(obj, {
// 拦截对象属性的读取,比如proxy.foo和proxy['foo']。
get(target, propKey, receiver) {
},
// 拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
set(target, propKey, value, receiver) {
},
// 拦截propKey in proxy的操作,返回一个布尔值。
has(target, propKey) {
},
// 拦截delete proxy[propKey]的操作,返回一个布尔值。
deleteProperty(target, propKey) {
},
// 拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
defineProperty(target, propKey, propDesc) {
},
// 其它
});
var proxy = new Proxy(target,handler)
var obj = new Proxy(obj, {
// 拦截对象属性的读取,比如proxy.foo和proxy['foo']。
get(target, propKey, receiver) {
},
// 拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
set(target, propKey, value, receiver) {
},
// 拦截propKey in proxy的操作,返回一个布尔值。
has(target, propKey) {
},
// 拦截delete proxy[propKey]的操作,返回一个布尔值。
deleteProperty(target, propKey) {
},
// 拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
defineProperty(target, propKey, propDesc) {
},
// 其它
});
this 指向问题
Proxy
代理的情况下,目标对象内部的 this
关键字会指向 Proxy
代理。
JS
const target = {
m: function () {
console.log(this === proxy);
}
};
const handler = {};
const proxy = new Proxy(target, handler);
target.m() // false
proxy.m() // true
const target = {
m: function () {
console.log(this === proxy);
}
};
const handler = {};
const proxy = new Proxy(target, handler);
target.m() // false
proxy.m() // true
Proxy
与 Object.defineProperty
区别
Proxy
代理整个对象,Object.defineProperty
只能代理某个属性;Proxy
可以监听对象新增属性;Proxy
可以监听数组新增修改;- 若对象内部属性要全部递归代理,
Proxy
可以只在调用的时候递归,而Object.definePropery
需要一次完成所有递归,性能比Proxy
差; Proxy
不兼容IE,Object.defineProperty
不兼容IE8及以下;Proxy
语法更简单;
问题
1. 为什么 Vue2
中 defineProperty
无法监听数组原生操作?
在 Vue2
中这是全量劫持了对象,出于性能考虑,没有全量劫持数组,Object.defineProperty
是可以监听数组操作的,但是存在些缺陷。对数组 length
赋值,或者通过数组下标修改元素的值,是不会响应式的,原因是不会触发 setter
方法。当然,length
属性的 configurable
为 false
是一个不可配置的,即不能通过劫持修改与删除。
举个例子:let arr = [1]
,改变其长度 arr.length = 10
,此时我们打印 arr[1]
结果为 undefined
,这个值肯定是无法被监听的。