Skip to main content

JS:proxy与reflect

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传入

这里体现两个重要信息:

  1. Proxy在构造对象时接受两个参数:targethandler
  2. 两个参数的类型必须是object

那问题来了,这两个参数target和handler分别表示什么呢 在最开始,我说过Proxy的本意是代理意思,表示由它来“代理”某些操作;网上还有另外一个更恰当的理解,就是:

可以将Proxy理解成“拦截”,在目标对象之前架设一层“拦截”,当外界对该对象的访问,都必须先通过这层拦截,正因为有了一种拦截机制,当外界的访问我们可以对进行一些操作(过滤或改写)

所以我们可以很好的理解,target表示的就是要拦截(代理)的目标对象;而handler是用来定制拦截行为

target很容易理解,关键就在handler里头到底可以填什么呢?分别用于拦截什么操作呢? 要想明白这点,就得回顾我们之前怎么操作对象

例如:

let obj = {
name: 'alice',
showName() {
console.log(`my name is ${this.name}`)
}
}
  1. 获取对象属性
console.log(obj.name)//alice
  1. 给对象添加属性
obj.age = 12;
  1. 判断属性是否在对象中
console.log('age' in obj)//true
  1. 删除对象属性
delete obj.age
  1. 通过各种方法遍历对象的所有属性
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
  1. 获取对象的某个属性的描述对象
let d = Object.getOwnPropertyDescriptor(obj,'name')
console.log(d)
//{value: "alice", writable: true, enumerable: true, configurable: true}
  1. 使用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,
}
})
  1. 获取一个对象的原型对象
Object.getPrototypeOf(obj)      
console.log(Object.getPrototypeOf(obj) === obj.__proto__)//true
  1. 设置某个对象的原型属性对象
Object.setPrototypeOf(obj,null);
//表示设置对象的原型为null,也可以传入其他对象作为其原型
  1. 让一个对象变得不可扩展,即不能添加新的属性
Object.preventExtensions(obj)
  1. 查看一个对象是不是可扩展的
console.log(Object.isExtensible(obj));//false,因为上面设置了该对象为不可扩展对象
  1. 如果对象为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]);
  1. 如果对象时作为构造函数时,则该对象可以用new生成出新的对象
function Person(){}
let p1 = new Person();

以上都是对对象的一些操作!

那回到我们得new Proxy(target,handler)中的handler,我们之前说了,handler是用于设置拦截行为的,其实拦截的内容就是上面这一系列的对象操作,当对象执行某个操作时,就会触发handler里面定义的东西

通过Proxy可以拦截到被代理的对象执行的相关操作;当被代理对象执行某个操作时,那么它会执行该操作所对应的handler里面的函数,有点像我代理你去做事情,这也是被叫做Proxy的本意

下面来看具体handler对象可以设置哪些参数,分别拦截被代理对象的哪些操作

  1. 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,这里是因为被代理了,如果不存在我就抛出错误

  1. set(target, propKey, value, receiver)
    拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。 3. has(target, propKey)
    拦截propKey in proxy的操作,返回一个布尔值。
  2. deleteProperty(target, propKey)
    拦截delete proxy[propKey]的操作,返回一个布尔值。
  3. ownKeys(target)
    拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。
  4. getOwnPropertyDescriptor(target, propKey)
    拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  5. defineProperty(target, propKey, propDesc)
    拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
  6. preventExtensions(target)
    拦截Object.preventExtensions(proxy),返回一个布尔值。
  7. getPrototypeOf(target)
    拦截Object.getPrototypeOf(proxy),返回一个对象。
  8. isExtensible(target)
    拦截Object.isExtensible(proxy),返回一个布尔值。
  9. setPrototypeOf(target, proto)
    拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  10. apply(target, object, args)
    拦截Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
  11. construct(target, args)
    拦截Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。

试一试🌰

Live Editor
Result
Loading...