一,监听对象的操作
1,使用defineProperty监听对象操作
const obj = {
name: "sun",
age: 20,
};
Object.keys(obj).forEach((key) => {
let value = obj[key];
Object.defineProperty(obj, key, {
get: function () {
console.log(`获取了${key}属性`);
return value;
},
set: function (newValue) {
console.log(`设置了${key}属性`);
value = newValue;
},
});
});
// console.log(obj.name); //获取了name属性 sun
// console.log(obj.age); //获取了age属性 20
obj.name = "lebron"; //设置了name属性
obj.age = 37; //设置了age属性
console.log(obj.name); //获取了name属性 lebron
console.log(obj.age); //获取了age属性 37问题
1,Object.defineProperty设计的初衷不是监听截止对象中所有属性的,在定义某些属性的时候初衷只是定义普通的属性,但是后面强行被添加数据属性描述符。
2,Object.defineProperty属性无法监听删除属性、添加属性等操作。
2,Proxy
在ES6中,新增了一个Proxy类,这个类从名字就可以看出来,是用于帮助我们创建一个代理的:
也就是说,如果我们希望监听一个对象的相关操作,那么我们可以先创建一个代理对象(Proxy对象);
之后对该对象的所有操作,都通过代理对象来完成,代理对象可以监听我们想要对原对象进行哪些操作;
const obj = {
name: "sun",
age: 20,
};
const objProxy = new Proxy(obj, {
get: function (target, key) {
console.log(`获取属性 ${key}`, target);
return target[key];
},
set: function (target, key, newValue) {
console.log(`设置属性 ${key}`, target);
target[key] = newValue;
},
});
console.log(objProxy.name);
console.log(objProxy.age);
objProxy.name = "lebron";
objProxy.age = 37;
// 获取属性 name { name: "sun", age: 20 }
// sun
// 获取属性 age { name: "sun", age: 20 }
// 20
// 设置属性 name { name: "sun", age: 20 }
// 设置属性 age { name: "lebron", age: 20 }Proxy的set和get捕获器如果我们想要侦听某些具体的操作,那么就可以在handler中添加对应的捕捉器(Trap) set和get分别对应的是函数类型; set函数有四个参数:
target:目标对象(侦听的对象);
property:将被设置的属性key;
value:新属性值;
- receiver:调用的代理对象;
get函数有三个参数:
target:目标对象(侦听的对象);
property:被获取的属性key;
receiver:调用的代理对象;
Proxy所有捕获器:13个
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.ownKeys():Object.getOwnPropertyNames 方法和Object.getOwnPropertySymbols 方法的捕捉器。
handler.has():in 操作符的捕捉器。
handler.get():属性读取操作的捕捉器。
handler.set():属性设置操作的捕捉器。
handler.deleteProperty():delete 操作符的捕捉器。
handler.apply():函数调用操作的捕捉器。
handler.construct():new 操作符的捕捉器。
Proxy的construct和applyconstruct和apply是应用于函数对象的
function foo() {
console.log("foo函数被调用了", this, arguments);
return "foo";
}
const fooProxy = new Proxy(foo, {
apply: function (target, thisArg, otherArgs) {
console.log("函数的apply侦听");
return target.apply(thisArg, otherArgs);
},
construct(target, argArray, newTarget) {
console.log(target, argArray, newTarget);
return new target();
},
});3,Reflect的作用
Reflect也是ES6新增的一个API,它是一个对象,字面的意思是反射作用它主要提供了很多操作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对象上;
Reflect的常见方法和Proxy是一一对应的,也是13个
Reflect的使用
const obj = {
name: "sun",
age: 20,
};
const objProxy = new Proxy(obj, {
get: function (target, key) {
console.log(`获取属性 ${key}`, target);
// 因为proxy的目的就是不要在源对象上面修改属性,而target【key】本质仍然是,所以可以使用Reflect
// 区别:Reflect下方法返回的是一个Boolean类型的值,可以用作判断设置是否成功,假设如果对象提前使用了freeze方法,是修改不了的。
// return target[key];
return Reflect.get(target, key);
},
set: function (target, key, newValue) {
console.log(`设置属性 ${key}`, target);
// target[key] = newValue;
Reflect.set(target, key, newValue);
},
has: function (target, key) {
console.log(`执行检测属性 ${key}是否存在`, target);
return Reflect.has(target, key);
},
deleteProperty: function (target, key) {
console.log(`执行删除${key}属性`);
Reflect.deleteProperty(target, key);
},
});
console.log(objProxy.name);
console.log(objProxy.age);
objProxy.name = "lebron";
objProxy.age = 37;
console.log("name" in objProxy);
delete objProxy.age;
console.log(obj);
// 获取属性 name { name: "sun", age: 20 }
// sun
// 获取属性 age { name: "sun", age: 20 }
// 20
// 设置属性 name { name: "sun", age: 20 }
// 设置属性 age { name: "lebron", age: 20 }
// 执行检测属性 name是否存在 { name: "lebron", age: 37 }
// true
// 执行删除age属性
// { name: "lebron" }Reflect的construct
4,Receiver的作用
我们发现在使用getter、setter的时候有一个receiver的参数,如果我们的源对象(obj)有setter、getter的访问器属性,那么可以通过receiver来改变里面的this;
const obj = {
_name: "sun",
get name() {
return this._name;
},
set name(newValue) {
this._name = newValue;
},
};
// 原来写法
// const objProxy = new Proxy(obj, {
// get(target, key) {
// console.log("执行了get捕获器");
// return Reflect.get(target, key);
// },
// });
// console.log(objProxy.name); // 执行了get捕获器 sun
// 该访问方法存在的问题:
// 执行objproxy中的get捕获器=>返回reflect.get(obj,key)=>执行obj中的get方法返回this._name
// 还是直接操作的obj._name,当需要对obj的访问做一些拦截的时候不好实现。
// 这时候可以通过receiver来改变obj中getter中this的指向。指向objProxy
// receiver
const objProxy = new Proxy(obj, {
get(target, key, receiver) {
console.log("执行了get捕获器");
return Reflect.get(target, key, receiver);
},
});
console.log(objProxy.name); // 执行了get捕获器 执行了get捕获器 sun