Proxy代理
一、基本概念
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
1.语法
const p = new Proxy(target, handler)
target
:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。handler
:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
2.方法
除了使用实例化的方式创建proxy,Proxy对象也为我们提供了方法进行创建
Proxy.revocable(target, handler);
target
:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。handler
:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。返回值
: {“proxy”: proxy, “revoke”: revoke} 返回一个包含了代理对象本身和它的撤销方法的可撤销 Proxy 对象。
- proxy
表示新生成的代理对象本身,和用一般方式 new Proxy(target, handler) 创建的代理对象没什么不同,只是它可以被撤销掉。 - revoke
撤销方法,调用的时候不需要加任何参数,就可以撤销掉和它一起生成的那个代理对象。
3.简单使用
let obj = {
name: 'lis',
age: 21
}
let handle = {}
let a
let proxy = new Proxy(obj, handle)
console.log(proxy.name); // lis
obj.name = 'dwq'
console.log(proxy.name); // dwq
proxy.age = 2
console.log(obj.age); // 2
proxy.address = '广州';
console.log(obj.address); // 广州
即使我们的handler不进行任何操作,代理也会将所有应用到它的操作转发到target对象上。同样对target对象的操作也会同步到proxy上。
二、handler 对象的方法
handler对象的方法有13个
handler.get():属性读取操作的捕捉器。
handler.set():属性设置操作的捕捉器。
handler.deleteProperty():delete 操作符的捕捉器。
handler.has():in 操作符的捕捉器。
handler.apply():函数调用操作的捕捉器。
handler.construct():new 操作符的捕捉器。
handler.defineProperty():Object.defineProperty 方法的捕捉器。
handler.getPrototypeOf():Object.getPrototypeOf 方法的捕捉器。
handler.setPrototypeOf():Object.setPrototypeOf 方法的捕捉器。
handler.isExtensible():Object.isExtensible 方法的捕捉器。
handler.preventExtensions():Object.preventExtensions 方法的捕捉器。
handler.getOwnPropertyDescriptor():Object.getOwnPropertyDescriptor 方法的捕捉器。
handler.ownKeys():Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。
这里只列举常用的几个,其他的可以参考 MDN
1.get()
handler.get() 方法用于拦截对象的读取属性操作。
(1)语法
let p = new Proxy(target, {
get(target, property, receiver) {}
});
参数:
target
:目标对象。property
:被获取的属性名。receiver
:Proxy或者继承Proxy的对象返回值
:get方法可以返回任何值,且返回值会作为读取prop属性得到的值。
(2)具体使用
我们可以在get中进行相关操作,比如对返回值进行修改等等
let target = {
name: 'lis'
}
let p = new Proxy(target, {
get(target, prop) {
console.log('get');
return prop in target? target[prop]: 'no value in target'
}
});
console.log(p.name); // lis
console.log(p.age); // no value in target
2.set()
handler.set() 方法用于捕获对象的属性值设置操作。
(1)语法
let p = new Proxy(target, {
set(target, property, value) {}
});
参数:
target
:目标对象。property
:要设置的属性名。value
:要设置的属性值。返回值
:set方法可以返回任何值。
(2)具体使用
我们可以在set中进行相关操作,比如vue中在此对视图进行更新
let target = {
name: 'lis'
}
let p = new Proxy(target, {
set(target, prop, value) {
console.log('set');
target[prop] = value
return true
}
});
p.name = 'dwq'
console.log(p.name); // dwq
p.age = 10
console.log(p.age); // 10
3.deleteProperty()
handler.deleteProperty() 方法用于拦截对对象属性的 delete 操作。
(1)语法
let p = new Proxy(target, {
deleteProperty(target, property) {}
});
参数:
target
:目标对象。property
:要被删除的属性名。返回值
:deleteProperty 返回一个 Boolean 类型的值,表示了该属性是否被成功删除。
(2)具体使用
该方法会拦截以下操作:
- 删除属性: delete proxy[foo] 和 delete proxy.foo
- Reflect.deleteProperty()
let target = {
name: 'lis',
age: 10,
address: '广州'
}
let p = new Proxy(target, {
deleteProperty(target, property) {
console.log('deleteProperty');
delete target[property]
return true
}
});
delete p.name
console.log(p.name); // undefined
delete p['age']
console.log(p.age); // undefined
Reflect.deleteProperty(p, 'address')
console.log(p.address); // undefined
4.has()
handler.has() 方法是针对 in 操作符的代理方法。
(1)语法
let p = new Proxy(target, {
has(target, property) {}
});
参数:
target
:目标对象。property
:是否在target的属性名。返回值
:返回一个 Boolean 类型的值,表示了该属性是否在target中。
(2)具体使用
我们可以在has中进行相关操作
let target = {
name: 'lis',
age: 10,
address: '广州'
}
let p = new Proxy(target, {
has(target, property) {
console.log('has');
if (property === 'name') {
return false
}
return property in target
}
});
console.log('name' in p); // false
console.log('age' in p); // true
console.log('address' in p); // true
console.log('phone' in p); // false
5.apply()
handler.set() 方法用于捕获对象的属性值设置操作。
(1)语法
let p = new Proxy(target, {
apply(target, thisArg, argumentsList) {}
});
参数:
target
:目标对象。thisArg
:被调用时的上下文对象。argumentsList
:被调用时的参数数组。返回值
:apply方法可以返回任何值。
(2)具体使用
我们可以在apply中进行相关操作,替换对原函数的调用
let target = function (name, age) {
return name + '-' + age
}
let p = new Proxy(target, {
apply(target, thisArg, arguments) {
if (arguments[0] === 'lis') {
return 'name error'
}
return target(...arguments);
}
});
console.log(p('lis', 21)); // name error
console.log(p('dwq', 23)); // dwq-23
其中第二个参数thisArg与原函数稍有不同。
let target = function () {
console.log(this)
}
let p = new Proxy(target, {
apply(target, thisArg) {
console.log(thisArg);
}
});
let obj = {
funcP: p,
funcT: target,
}
p()
target()
obj.funcP()
obj.funcT()
全局上下文调用时,原生函数this为Window,而proxy为undefined。这一点需要注意一下。
6.construct()
handler.construct() 方法用于拦截new 操作符. 为了使new操作符在生成的Proxy对象上生效,new target 必须是有效的。
(1)语法
let p = new Proxy(target, {
construct(target, argumentsList, newTarget) {}
});
参数:
target
:目标函数。argumentsList
:被获取的属性名。newTarget
:最初被调用的构造函数代理,就上面的例子而言是p。返回值
:construct方法必须返回一个对象。
(2)具体使用
我们可以在construct中进行相关操作
let targetFunc = function (name) {
this.name = name
}
let p = new Proxy(targetFunc, {
construct(target, arguments, newTarget) {
console.log(newTarget);
if (arguments[0] === 'lis') {
return {
name: 'error'
}
}
return new target(...arguments)
}
});
console.log(new p('lis'));
console.log(new p('dwq'));