JavaScript中的Proxy
前言 🔗
这个和之前的Reflect有一定的关系,也是Vue3实现的一个很重要的API,学学学。
Proxy 🔗
顾名思义,就是一个代理对象,通过一组配置函数来拦截操作从而使得以源对象得以被扩展。
Proxy是一个构造函数
由两个参数
obj需要代理的对象handler一组由特定的处理函数组成的对象,如果没有指定,则会使用源对象的默认行为。
var o = {};
var proxy = new Proxy(o,{});在Reflect上的方法,Proxy都可以拦截,具体包括
getPrototypeOfsetPrototypeOfisExtensiblepreventExtensiblegetOwnPropertyDescriptordefinePropertyhasgetsetdeletePropertyownKeysapplyconstruct
getPrototypeOf() 🔗
读取代理对象的原型时,这个方法就会调用
有一个参数
obj源对象
该函数必须返回一个对象或者null
var o = {};
var prototype = {
name:"prototype"
};
var proxy = new Proxy(o,{
getPrototypeOf(target){
return prototype;
}
});
console.log(Reflect.getPrototypeOf(proxy) === prototype); // 打印true可以触发该函数的操作包括
Object.getPrototypeOfReflect.getPrototypeOf__proto__Object.prototype.isPrototypeOfinstanceof
var proxy = new Proxy({},{
getPrototypeOf(target){
console.log("调用了");
return Reflect.getPrototypeOf(target);
}
});
// 打印了5次"调用了"
Object.getPrototypeOf(proxy);
Reflect.getPrototypeOf(proxy);
proxy.__proto__;
Object.prototype.isPrototypeOf(proxy);
proxy instanceof Object;setPrototypeOf() 🔗
设置代理对象的原型时,这个方法就会调用。
有两个参数
obj源对象prototype将设置的原型对象
该函数在设置prototype成功返回true,失败返回false
var proxy = new Proxy({},{
setPrototypeOf(obj,prototype){
console.log("设置了prototype");
return Reflect.setPrototypeOf(obj,prototype);
}
});
Reflect.setPrototypeOf(proxy,{});可以触发该函数的操作包括
Reflect.setProtytpeOfObject.setPrototypeOf
isExtensible() 🔗
查询代理对象是否可以扩展时,这个方法就会调用。
有一个参数
obj源对象
返回值必须为一个boolean值或者可以转为Boolean的值。
var proxy = new Proxy({},{
isExtensible(obj){
console.log("查询了是否能够扩展");
return Reflect.isExtensible(obj);
}
});
Reflect.isExtensible(proxy);可以触发该函数的操作包括
Reflect.isExtensibleObject.isExtensible
preventExtensions() 🔗
禁止代理对象扩展时,这个方法就会调用。
有一个参数
obj源对象
返回值必须为一个boolean值或者可以转为boolean的值。
var proxy = new Proxy({},{
preventExtensions(obj){
console.log("禁止了扩展");
return Reflect.preventExtensions(obj);
}
});
Reflect.preventExtensions(proxy);可以触发该函数的操作包括
Reflect.preventExtensionsObject.preventExtensions
getOwnPropertyDescriptor() 🔗
获取对象属性的属性描述符时,这个方法会调用。
有两个参数
obj源对象propertyKey需要获取属性描述符的属性名
返回值必须为一个对象或者undefined
var proxy = new Proxy({
name:"lwf"
},{
getOwnPropertyDescriptor(obj,propertyKey){
console.log("获取了属性描述符");
return Reflect.getOwnPropertyDescriptor(obj,propertyKey);
}
});
Reflect.getOwnPropertyDescriptor(proxy,"name"); // 打印可以触发该函数的操作包括
Reflect.getOwnPropertyDescriptorObject.getOwnPropertyDescriptor
defineProperty() 🔗
在定义代理对象的属性时,这个方法会调用。
有三个参数
obj源对象propertyKey将设置的属性名descriptor将设置属性的属性描述符
返回值必须为一个boolean值或者可以转为boolean的值。
var proxy = new Proxy({},{
defineProperty(obj,propertyKey,descriptor){
console.log("设置了对象的属性");
return Reflect.defineProperty(obj,propertyKey,descriptor);
}
});
Reflect.defineProperty(proxy,"name",{
value:"lwf"
}); // 打印
console.log(proxy.name);可以触发该函数的操作包括
Reflect.definePropertyObject.definePropertyproxyObj.propertyKey = value
has() 🔗
在检查属性是否存在对象中时,这个方法会调用。
有两个参数
obj源对象propertyKey需要检查的属性名
返回值必须为一个boolean值或者可以转为boolean的值。
var proxy = new Proxy({},{
has(obj,propertyKey){
console.log("检查了属性是否存在");
return propertyKey in obj;
}
});
"name" in proxy; // 打印可以触发该函数的操作包括
propertyKey in proxypropertyKey in Object.create(proxy)Reflect.haswith操作
get() 🔗
在读取代理对象属性时,这个方法会调用。
有三个参数
obj源对象propertyKey要获取的属性名context要绑定到getter的上下文,默认为调用对象本身。
返回值可以为任何值。
var proxy = new Proxy({},{
get(obj,propertyKey,context){
console.log("获取了属性值");
return Reflect.get(obj,propertyKey,context);
}
});
proxy.name; // 打印可以触发该函数的操作包括
proxy[propertyKey]或者proxy.propertyKeyObject.create(proxy)[propertyKey]或者Object.create(proxy).propertyKeyReflect.get
set() 🔗
在设置代理对象属性值时,这个方法会调用。
有四个参数
obj源对象propertyKey设置的属性名value设置的属性值context要绑定到setter的上下文,默认为调用对象本身。
TIPS: 对于get和set,其中context参数的传入通常是proxy对象本身,但是对于处于原型链上的proxy,传入的就不是proxy对象了,而是触发操作的那个对象,比如,obj.propertykey = value,obj不是代理对象,但是obj的原型链上存在代理对象proxy,那传入context参数的就是obj而不是proxy了。而对于通过Reflect.set操作触发,context参数就跟set函数指定的一样了。
返回值必须为一个boolean值或者可以转为boolean的值。
var proxy = new Proxy({},{
set(obj,propertyKey,value,context){
console.log("调用了设置值");
return Reflect.set(obj,propertyKey,value,context);
}
});
proxy.name = "lwf"; // 打印可以触发该函数的操作包括
proxy[propertyKey] = value或者proxy.propertyKey = valueObject.create(proxy)[propertyKey] = value或者Object.create(proxy).propertyKey = valueReflect.set
deteleProperty() 🔗
在删除对象的属性时,这个方法会调用
有两个参数
obj源对象propertyKey将删除的属性名
返回值必须为一个boolean值或者可以转为boolean的值。
var proxy = new Proxy({},{
deleteProperty(obj,propertyKey){
console.log("删除了属性");
return Reflect.deleteProperty(obj,propertyKey);
}
});
delete proxy.name; // 打印可以触发该函数的操作包括
delete proxy[propertyKey]或者delete proxy.propertyKeyReflect.deleteProperty
ownKeys() 🔗
获取代理对象属性名组成的数组时,这个方法会调用
有一个参数
obj源对象
返回值必须为一个可枚举的对象。
var proxy = new Proxy({},{
ownKeys(obj){
console.log("获取了属性名组成的数组");
return Reflect.ownKeys(obj);
}
});
Reflect.ownKeys(proxy); // 打印可以触发该函数的操作包括
Object.getOwnPropertyNamesObject.getOwnPropertySymbolsReflect.ownKeysObject.keys
apply() 🔗
在对代理对象进行函数调用时,这个方法会调用
有一个参数
fn源函数context执行函数的上下文args参数的数组
返回值可以为任意值。
function fn(msg){
console.log(msg);
}
var proxy = new Proxy(fn,{
apply(fn,context,args){
console.log("执行了函数");
return fn.apply(context,args);
}
});
proxy("hello world"); // 打印可以触发该函数的操作包括
proxy(arg1,arg2,...,argN)Function.prototype.apply或者Function.prototype.callReflect.apply
construct() 🔗
以代理对象为构造器或者原型上存在代理对象的构造器生成对象时,这个方法会调用
有三个参数
obj源对象(可以被new)args参数数组newTarget被调用的构造函数
返回值必须为一个对象
function fn(name){
this.name = name;
}
var proxy = new Proxy(fn,{
construct(obj,args,newTarget){
console.log("构造了新对象");
return Reflect.construct(obj,args);
}
});
var p = new proxy("lwf"); // 打印
console.log(p.name); // 打印 "lwf"可以触发该函数的操作包括
new proxy(arg1,arg2,...,argN)Reflect.construct
后记 🔗
学完感觉我能看懂vue3源码了。