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源码了。