Proxy可以理解为对一个对象的代理,在操作目标对象前进行拦截,执行不同的函数,先看看它的简单用法
var target = {a:"John"}
var proxy = new Proxy(target,{
get:(tar,key)=>("Tony")
})
对于上面代码,你可以这样理解,首先代理的目标对象是target,但其实你可以理解为它先把target对象赋值给了proxy变量,然后再代理这个proxy对象,然后再看看Proxy构造函数的第二个参数,他是一个对象,这个对象里面有一个get方法,可以这样理解,第二个参数就是一个处理器对象,处理器对象包含了许多的拦截方法,比如get,set,apply等,这些后面会讲到,而这些拦截方法的意义就是当你对目标对象进行对应操作时,就会触发这些对象的拦截方法,比如你要访问目标对象的a属性proxy.a,此时get方法就会出来拦截,给你返回了Tony,而不是a属性原本的值John,这就是拦截函数的作用,接下来看看具体的各种拦截函数的使用
get
get方法用于拦截对目标对象的属性的读取操作,接收三个参数:目标对象,属性名和proxy实例本身 下面看看一个用proxy实现数组负数索引访问的例子
var target = [1,2,3,4,5]
var handler = {
get:(tar,idx)=>{
idx = (idx/1)%tar.length
if(idx<0){
return tar[tar.length + idx]
}
}
}
var proxy = new Proxy(target,handler)
proxy[-1];//5
注意:
- 如果目标对象的属性不可修改,不可配置,那么通过proxy拦截访问操作就会报错
set
set方法用于拦截对目标对象的赋值操作,比如我们可以控制一个属性值可修改的范围,set接收四个参数:目标对象,要修改的属性,要修改的值,proxy实例本身
var target = {age:18}
var handler = {
set:(tar,key,value)=>{
if(key == "age"){
if(value>100 || value<0)throw new Error("the age seems invalid")
}
tar[key] = value
}
}
var proxy = new Proxy(target,handler)
proxy.age = 200;//error
注意:
- 如果目标对象某个属性不可写,那么set方法不起作用
- 严格模式下,set方法需要返回true,否则报错
apply
apply方法拦截函数的调用,call和apply操作 apply方法接收三个参数:目标对象,目标对象的上下文(即谁调用了目标对象)和目标对象的参数数组(即传入目标对象函数的参数) 看一下使用的例子
var targetfn = function(x,y){
return x*y
}
var handler = {
apply:(tar,ctx,args)=>{
return args.reduce((total,now)=>(total+now))
}
}
var proxy = new Proxy(targetfn,handler)
proxy(3,4)//7
可见proxy拦截了原本函数的乘法操作,返回了所有数相加之后的结果
其他的方法
还有很多方法这里不详细讲解,可以看看阮一峰es6文章的proxy部分
- get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。
- set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
- has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
- deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
- ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
- getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
- defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
- preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
- getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
- isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
- setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
- apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
- construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
this
当目标对象被代理后,目标对象内部的this就会指向proxy实例
var target = {
th:function(){console.log(this == proxy)}
}
var proxy = new Proxy(target,{})
target.th();//false
proxy.th();//true
另外,拦截函数中的this会指向处理器对象即handler本身
var handler = {
get:function(){console.log(this == handler)}
}
var proxy = new Proxy({},handler)
proxy.a//true undefined