Proxy
字面上是代理的意思
通过typeof
来检测一下
console.log(typeof Proxy)
得知Proxy
是定义在window
上的全局变量,类型为function
并且首字母大写,我们可以猜出它是一个构造函数或者说是一个类
那本身可以执行吗
let res = Proxy()
以上代码会报错:Uncaught TypeError: Constructor Proxy requires 'new'
原来使用Proxy()
执行,需要配合new
好吧,那我们来new
一个Proxy
子类对象
let p = new Proxy()
以上代码执行也会抛出错误:Uncaught TypeError: Cannot create proxy with a non-object as target or handler
,意思是创建proxy
对象时,不能使用不是对象的东西作为target
或者handler
传入
这里体现两个重要信息:
-
Proxy
在构造对象时接受两个参数:target
和handler
- 两个参数的类型必须是
object
那问题来了,这两个参数target
和handler
分别表示什么呢
在最开始,我说过Proxy
的本意是代理意思,表示由它来“代理”某些操作;网上还有另外一个更恰当的理解,就是:
可以将
Proxy
理解成“拦截”,在目标对象之前架设一层“拦截”,当外界对该对象的访问,都必须先通过这层拦截,正因为有了一种拦截机制,当外界的访问我们可以对进行一些操作(过滤或改写)
所以我们可以很好的理解,target
表示的就是要拦截(代理)的目标对象;而handler
是用来定制拦截行为
target很容易理解,关键就在handler
里头到底可以填什么呢?分别用于拦截什么操作呢?
要想明白这点,就得回顾我们之前怎么操作对象
例如:
let obj = {
name: 'alice',
showName() {
console.log(`my name is ${this.name}`)
}
}
- 获取对象属性
console.log(obj.name)//alice
- 给对象添加属性
obj.age = 12;
- 判断属性是否在对象中
console.log('age' in obj)//true
- 删除对象属性
delete obj.age
- 通过各种方法遍历对象的所有属性
console.log(Object.getOwnPropertyNames(obj));//["name", "showName"]
console.log(Object.getOwnPropertySymbols(obj));//[]
console.log(Object.keys(obj))//["name", "showName"]
for (let key in obj){
console.log(key)
}//分别打印name showName
- 获取对象的某个属性的描述对象
let d = Object.getOwnPropertyDescriptor(obj,'name')
console.log(d)
//{value: "alice", writable: true, enumerable: true, configurable: true}
- 使用Object身上的方法,为某个对象添加一个或多个属性
Object.defineProperty(obj,'age',{
value:12,
writable:true,
enumerable:true,
configurable:true
})
Object.defineProperties(obj,{
showAge:{
value:function(){console.log(`我今年${this.age}岁了`)},
writable:true,
enumerable:true,
configurable:true,
},
showInfo:{
value:function(){console.log(`我叫${this.name},我今年${this.age}岁了`)},
writable:true,
enumerable:true,
configurable:true,
}
})
- 获取一个对象的原型对象
Object.getPrototypeOf(obj)
console.log(Object.getPrototypeOf(obj) === obj.__proto__)//true
- 设置某个对象的原型属性对象
Object.setPrototypeOf(obj,null);
//表示设置对象的原型为null,也可以传入其他对象作为其原型
- 让一个对象变得不可扩展,即不能添加新的属性
Object.preventExtensions(obj)
- 查看一个对象是不是可扩展的
console.log(Object.isExtensible(obj));//false,因为上面设置了该对象为不可扩展对象
- 如果对象为function类型,function类型的对象可以执行被执行符号()以及.call()和.apply()执行
function fn(...args){
console.log(this,args)
}
fn(1,2,3);
fn.call(obj,1,2,3);
fn.apply(obj,[1,2,3]);
- 如果对象时作为构造函数时,则该对象可以用new生成出新的对象
function Person(){}
let p1 = new Person();
以上都是对对象的一些操作!
那回到我们得new Proxy(target,handler)
中的handler
,我们之前说了,handler
是用于设置拦截行为的,其实拦截的内容就是上面这一系列的对象操作,当对象执行某个操作时,就会触发handler
里面定义的东西,
通过Proxy
可以拦截到被代理的对象执行的相关操作;当被代理对象执行某个操作时,那么它会执行该操作所对应的handler里面的函数,有点像我代理你去做事情,这也是被叫做Proxy
的本意
下面来看具体handler
对象可以设置哪些参数,分别拦截被代理对象的哪些操作
get(target, propKey, receiver)
get方法用于拦截某个属性的读取操作,比如proxy.foo
和proxy['foo']
var person = {
name: "Alice"
};
var proxy = new Proxy(person, {
get: function(target, propKey) {
if (propKey in target) {
return target[propKey];
} else {
throw new ReferenceError(`Prop name ${propKey} does not exist.`);
}
}
});
proxy.name // "Alice"
proxy.age // 抛出错误:Uncaught ReferenceError: Prop name age does not exist.
如果没有这个拦截函数,访问不存在的属性,只会返回undefined,这里是因为被代理了,如果不存在我就抛出错误
-
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
循环,返回一个数组。 -
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)
。
待续...