![](/style/images/good.png)
![](/style/images/bad.png)
JS进阶 | Proxy代理对象
source link: https://juejin.cn/post/7060864025373966343
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」
JS进阶系列文章
什么是Proxy
Proxy
对象用于创建一个对象的代理,是用于监听一个对象的相关操作。代理对象可以监听我们对原对象的操作。
接下来我们将通过一个监听对象的属性操作来认识学习下什么是Proxy
。
Proxy对象需要传入两个参数,分别是需要被Proxy
代理的对象和一系列的捕获器(PS:下面会讲)。
const obj={
name:'_island'
}
const objProxy=new Proxy(obj,{});
console.log(objProxy);
复制代码
打印出来可以看到的是一个Proxy
对象。下面我们开始看看Proxy
中的捕获器对象。
Proxy捕获器
在实例化Proxy
对象时,第二个参数传入的是捕获器集合,我们在其对象内定义一个get
捕获器,用于监听获取对象值的操作。
// 定义一个普通的对象obj
const obj = {
name: "_island"
};
// 代理obj这个对象,并传入get捕获器
const objProxy = new Proxy(obj, {
// get捕获器
get: function (target, key) {
console.log(`捕获到对象获取${key}属性的值操作`);
return target[key];
},
});
// 通过代理对象操作obj对象
console.log(objProxy.name);
// 捕获到对象获取name属性的值操作
// _island
复制代码
在objProxy
对象的拦截器中新增一个捕获器set
,用于监听对象的某个属性被设置时触发。
// set捕获器
set: function (target, key, val) {
console.log(`捕获到对象设置${key}属性的值操作,新值为${val}`);
target[key] = val;
}
console.log(objProxy.name = "QC2125");
// 捕获到对象设置name属性的值操作,新值为QC2125
console.log(objProxy.name);
// 捕获到对象获取name属性的值操作
// QC2125
复制代码
如果不想这个属性被设定这个值,你可以抛出异常告诉开发者,该值不能被设定。
set: function (target, key, val) {
if (key==='age' && typeof val === "number") {
target[key] = val;
} else {
throw new TypeError("该属性的值必须是Number类型");
}
}
复制代码
我们也可以监听对象是否调用了getPrototypeOf
操作,使用getPrototypeOf
捕获器即可。
// 监听getPrototypeOf
getPrototypeOf: function () {
console.log(`监听到对象getPrototypeOf操作`);
},
复制代码
在Proxy
中共有13
个捕获器,它们用于我们对对象、函数的方法调用监听。下面是Proxy
捕获器以及它们的触发条件。
对象中的方法对应触发条件handler.getPrototypeOf()Object.getPrototypeOf
方法的捕捉器handler.setPrototypeOf()Object.setPrototypeOf
方法的捕捉器handler.isExtensible()Object.isExtensible
方法的捕捉器handler.preventExtensions()Object.preventExtensions
方法的捕捉器handler.getOwnPropertyDescriptor()Object.getOwnPropertyDescriptor
方法的捕捉器。handler.defineProperty()Object.defineProperty
方法的捕捉器handler.has()in
操作符的捕捉器handler.get()属性读取操作的捕捉器handler.set()属性设置操作的捕捉器handler.deleteProperty()delete
操作符的捕捉器handler.ownKeys()Object.getOwnPropertyNames
方法和 Object.getOwnPropertySymbols
方法的捕捉器handler.apply()函数被apply
调用操作的捕捉器handler.construct()new
操作符的捕捉器
this指向的问题
Proxy
对象可以对我们的目标对象进行访问,但没有做任何拦截时,也不能保证与目标对象的行为一致,因为目标对象内部的this
会自动改变为Proxy
代理对象。我们看下面这个例子就知道了。
const obj={
name:'_island',
foo:function(){
return this === objProxy
}
}
const objProxy=new Proxy(obj,{})
console.log(obj.foo()); // false
console.log(objProxy.foo()); // true
复制代码
对象监听案例
某些场景下,需要监听一个对象的操作,当这个操作触发时执行另外的一个函数,就像vue2
中的watchApi
,它可以监听data
数据中某个属性的改变并操作指定的函数。
我们看看下面这份代码,在ES5
中使用Object.defineProperty
(对象属性描述符)对对象的监听,将一个对象进行遍历,并设定getter
、setter
方法进行监听和拦截。
// 定义一个Object对象
const obj = {
name: "_island",
age: 18
};
Object.keys(obj).forEach((key) => {
let val = obj[key];
Object.defineProperty(obj, key, {
get: function () {
console.log(key + "调用了get方法");
return val;
},
set: function (newVal) {
console.log(key + "调用了set方法");
val = newVal;
}
});
});
// 操作obj对象
obj.name = "QC2125";
// name调用了set方法
obj.age = 30;
// age调用了set方法
console.log(obj.name);
// name调用了get方法
// QC2125
复制代码
Object.defineProperty
的设计初衷并不是为了去监听拦截一个对象中的属性,且他也实现不了更加丰富的操作,例如添加、删除属性等操作。所以在ES6
中新增了Proxy
对象,用于监听Object
、Function
的操作。
我们将上面通过Object.defineProperty
实现对象监听的方法修改成Proxy
方案。在Vue3框架中的响应式原理也是用到了Proxy对象进行对属性的监听操作。
const obj = {
name: "_island",
age: 18
};
const objProxy = new Proxy(obj, {
// 获取值时的捕获器
get: function (target, key) {
console.log(`监听到了${key}被获取值`);
return target[key];
},
// 设置值时的捕获器
set: function (target, key, newValue) {
console.log(`监听到了${key}被设置值`);
target[key] = newValue;
}
});
console.log(objProxy.name);
// 监听到了name被获取值
// _island
console.log(objProxy.age);
// 监听到了age被获取值
// 18
objProxy.name = "QC2125";
// 监听到了name被设置值
console.log(objProxy.name);
// 监听到了name被获取值
// QC2125
复制代码
proxy
是一个的代理对象,它可以代理我们对原目标的操作。相比Object.defineProperty
方法,Proxy
监听的事件更加方便。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK