ES6中的 Proxy 和 Reflect
Proxy
1.监听对象的操作
有一个需求:有一个对象,我们希望监听这个对象中的属性被设置或获取的过程。
- ES6前可以通过属性描述符中的存取属性描述符(
Object.defineProperty
)来做; - 还有代理
Proxy
1.1 属性描述符实现
使用属性描述符中的存取属性描述符:get
函数和set
函数。
const obj = {
name: "fct",
age: 18
}
// 监听对象某个元素的改变
// Object.defineProperty(obj, "name", {
// get() {
// console.log("监听到obj对象的name属性被访问了")
// },
// set() {
// console.log("监听到obj对象的name属性被设置值")
// }
// });
// 监听对象所有元素的改变
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj, key, {
get() {
console.log(`监听到obj对象的${key}属性被访问了`)
return value
},
set(newValue) {
console.log(`监听到obj对象的${key}属性被设置值`)
value = newValue;
}
});
});
obj.name = "wyt";
obj.age = 21;
console.log(obj.name);
console.log(obj.age);
obj.height = 1.0;
缺点:
- 首先,
Object.defineProperty
设计的初衷,不是为了去监听一个对象中所有的属性的。- 我们在定义某些属性的时候,初衷其实是定义普通的属性,但是后面我们强行将它变成了数据属性描述符。
- 其次,如果我们想监听更加丰富的操作,比如新增属性、删除属性,那么
Object.defineProperty
是无能为力的。 - 所以我们要知道,存储数据描述符设计的初衷并不是为了去监听一个完整的对象。
1.2 Proxy监听
在ES6中,新增了一个Proxy 类,是用于帮助我们创建一个代理的:
- 也就是说,如果我们希望监听一个对象的相关操作,那么我们可以先创建一个代理对象(Proxy对象);
- 之后对该对象的所有操作,都通过代理对象来完成,代理对象可以监听我们想要对原对象进行的操作;
基本使用:const proxyObj(代理对象) = new Proxy('对象名', 捕获器对象)
Proxy监听对象属性变化:
const obj = {
name: "fct",
age: 18
}
const objProxy = new Proxy(obj, {
// 获取值时的捕获器
get(target, key, receiver) {
// target为侦听的对象,key为当前操作的属性key
console.log(`监听到对象的${key}属性被访问了`, target)
return target[key];
},
// 设置值时的捕获器
set(target, key, newValue, receiver) {
// newValue 为新属性值
console.log(`监听到对象的${key}属性被设置值`, target)
target[key] = newValue;
}
})
console.log(objProxy.name);
console.log(objProxy.age);
objProxy.name = "wyt";
objProxy.age = 21;
console.log(obj.name);
console.log(obj.age);
2. Proxy的捕获器
-
get
(获取值)捕获器 -
set
(设置值)捕获器 -
in
操作捕获器const objProxy = new Proxy(obj, { // 监听in的捕获器 has(target, key) { console.log(`监听到对象的${key}属性in操作`, target) return key in target; }, }); // in操作符,name属性是否在objProxy对象中 console.log("name" in objProxy);
-
delete
删除捕获器const objProxy = new Proxy(obj, { // 监听delete的捕获器 deleteProperty: function(target, key) { console.log(`监听到对象的${key}属性被删除操作`, target) delete target[key]; } }); // delete操作 delete objProxy.name;
-
Object.getPrototypeOf
获取原型方法的捕获器 -----》getPrototypeOf()
-
Object.setPrototypeOf
设置原型方法的捕获器 -----》setPrototypeOf()
-
Object.isExtensible
判断对象是否可扩展方法的捕获器 -----》isExtensible()
-
Object.preventExtensions
阻止对象可扩展方法的捕获器 -----》preventExtensions()
-
Object.getOwnPropertyDescriptor
获取对象属性描述符方法的捕获器 -----》getOwnPropertyDescriptor()
-
Object.defineProperty
设置对象属性描述符方法的捕获器-----》defineProperty()
-
Object.getOwnPropertyNames
获取对象属性名方法和Object.getOwnPropertySymbols
获取对象Symbol属性的捕获器-----》ownKeys()
-
(函数对象) 函数调用操作的捕获器 -----》
apply()
function foo() {} const fooProxy = new Proxy(foo, { apply: function (target, thisArg, argArray) { console.log("对foo函数进行了调用") return target.apply(thisArg, argArray) } }) fooProxy(); fooProxy.apply({}, ["abc", "cba"]);
-
(函数对象) new 操作符的捕获器 -----》
construct
function foo() {} const fooProxy = new Proxy(foo, { construct: function (target, argArray, newTarget) { console.log("对foo函数进行了new调用") return new target(...argArray) } }) new fooProxy("abc", "cba");
Reflect
Reflect
也是ES6新增的一个API,它是一个对象,字面的意思是反射。
1. Reflect 作用
作用:
- 它主要提供了很多操作JavaScript对象的方法,有点像Object中操作对象的方法;
- 比如
Reflect.getPrototypeOf(target)
类似于Object.getPrototypeOf()
; - 比如
Reflect.defineProperty(target, propertyKey, attributes)
类似于Object.defineProperty()
;
我们有Object可以做这些操作,那么为什么还需要有Reflect这样的新增对象呢?
- 这是因为在早期的ECMA规范中没有考虑到对 对象本身 的操作如何设计会更加规范,所以将这些API放到了Object上面;
- 但是Object作为一个构造函数,这些操作实际上放到它身上并不合适;
- 另外还包含一些类似于in、delete操作符,让JS看起来是会有一些奇怪的;
- 所以在ES6中新增了
Reflect
,让这些操作都集中到了Reflect
对象上;
2. Reflect的方法
Reflect的方法和Proxy的捕获器方法是一一对应的。
使用:
const obj = {
name: "fct",
age: 18
}
const objProxy = new Proxy(obj, {
get: function (target, key, receiver) {
console.log("get---------")
return Reflect.get(target, key)
},
set: function (target, key, newValue, receiver) {
console.log("set---------")
// target[key] = newValue
// 拥有Boolean返回值
const result = Reflect.set(target, key, newValue)
if (result) {
} else {
}
}
})
objProxy.name = "wyt"
console.log(objProxy.name)
3. receiver参数的作用
const obj = {
_name: "fct",
get name() {
return this._name
},
set name(newValue) {
this._name = newValue
}
}
const objProxy = new Proxy(obj, {
get: function (target, key, receiver) {
// target 是源对象。即 obj对象
// receiver是创建出来的代理对象,即当前 objProxy
console.log("get方法被访问--------", key);
// 不传递 receiver 参数,obj对象中的 this 指向 obj对象,不能对 _name属性进行拦截
// return Reflect.get(target, key);
// 传递 receiver 参数,this 指向 objProxy对象,可对 _name属性再进行拦截
return Reflect.get(target, key, receiver);
},
set: function (target, key, newValue, receiver) {
console.log("set方法被访问--------", key)
Reflect.set(target, key, newValue);
Reflect.set(target, key, newValue, receiver);
}
})
console.log(objProxy.name);
objProxy.name = "wyt";
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步