用存储属性描述符来监听这个对象中的属性被设置或获取的过程:
const obj={
name:"kaisa",
age:18,
height:1.88
}
const keys=Object.keys(obj);
for(const key of keys){
let value=obj[key];
Object.defineProperty(obj,key,{
//setter
set:function(newValue){
console.log(`${key}属性设置成了${newValue}`);
value=newValue;
},
//getter
get:function(){
console.log(`获取${key}的值`);
return value;
}
})
}
//测试
obj.name="asd"
console.log(obj.age);
也就是说,我们可以存储属性描述符来监听对象,但是,有以下缺点:
Object.defineProperty
设计的初衷,不是为了去监听截止一个对象中所有的属性的Object.defineProperty
无法做到存储数据描述符设计的初衷并不是为了去监听一个完整的对象
Proxy
对象)我们可以将上面的案例用Proxy
来实现一次:我们需要new Proxy对象,并且传入需要侦听的对象以及一个处理对象,处理对象可以称之为handler
:
const p = new Proxy(target, handler);
我们之后的操作都是直接对Proxy的操作,而不是原有的对象,因为我们需要在handler里面进行侦听。
const obj={
name:"kaisa",
age:18,
height:1.88
}
//创建proxy对象
const objPro=new Proxy(obj,{})
//对obj的操作都应该到objPro这个代理进行操作
console.log(objPro.name);
objPro.name="asd"
console.log(objPro.name);
//kaisa
//asd
如果我们想要侦听某些具体的操作,就可以在handler中添加对应的捕获器(Trap)。
set和get分别对应的是函数类型。
set的四个参数:
target
:目标对象(侦听的对象)property
:将被设置的属性keyvalue
:新属性值receiver
:调用的代理对象get的三个参数:
target
:目标对象(侦听的对象)property
:将被设置的属性keyreceiver
:调用的代理对象代码:
const obj={
name:"kaisa",
age:18,
height:1.88
}
const objPro=new Proxy(obj,{
set:function(target,key,newValue){
console.log(`${key}属性被设置为${newValue}`);
target[key]=newValue;
},
get:function(target,key){
console.log(`${key}属性被访问`);
return target[key];
}
});
console.log(objPro.name);
objPro.name="asd"
console.log(objPro.name);
// name属性被访问
// kaisa
// name属性被设置为asd
// name属性被访问
// asd
handler.has()——监听 in 的捕获器
const obj={
name:"kaisa",
age:18,
height:1.88
}
const objPro=new Proxy(obj,{
has:function(target,key){
console.log(`箭头in判断${key}属性`);
return key in target;
}
})
console.log("name" in obj);
console.log("namee" in obj);
//true
//false
handler.deleteProperty()——delete 操作符 的捕获器
get和set是没办法监听属性删除的。
const obj={
name:"kaisa",
age:18,
height:1.88
}
const objPro=new Proxy(obj,{
deleteProperty:function(target,key){
console.log(`${key}属性被删除了`);
delete obj[key]
}
})
delete objPro.name
console.log(obj);
//name属性被删除了
//{age: 18, height: 1.88}
所有捕获器:
construct和apply是应用于函数对象的。
监听apply:
function foo(num1,num2){
console.log(this,num1,num2);
}
const fooProxy=new Proxy(foo,{
apply:function(target,thisArg,otherArg){
console.log(`监听:foo函数执行了apply操作`);
target.apply(thisArg,otherArg);
}
})
fooProxy.apply(123,[10,20])
//监听:foo函数执行了apply操作
//Number {123} 10 20
//显然this是对象123,num1是10,num2是20
监听new:
function foo(num1){
console.log(num1);
}
const fooProxy=new Proxy(foo,{
construct:function(target,otherArr){
console.log(`foo函数进行了new操作`);
return new target(otherArr)
}
})
new fooProxy(123);
//foo函数进行了new操作
//[123]
Reflect是ES6新增的一个API,它是一个对象,字面的意思是反射。
Object和Reflect对象之间的API关系:比较 Reflect 和 Object 方法
作用:
Reflect.getPrototypeOf(target)
类似于 Object.getPrototypeOf(traget)
为什么要有Reflect:
Reflect
,让我们将这些操作都集中到了Reflect对象上,在使用Proxy
时,可以做到不操作原对象和Proxy一一对应的,也是13个:
上面的案例用Reflect写:
const obj={
name:"kaisa",
age:18,
height:1.88
}
const objPro=new Proxy(obj,{
set(target,key,newValue){
console.log(`${key}属性发生改变`);
//Reflect没有操作原对象
const isSuccess=Reflect.set(target,key,newValue);
if(isSuccess){
console.log(`${key}属性设置set成功`);
}else {
console.log(`${key}属性设置set失败`);
}
},
get(target,key){
console.log(`获取get${key}属性`);
return target[key];
},
has(target,key){
console.log(`判断对象是否有has${key}属性`);
return Reflect.has(target,key);
},
deleteProperty(target,key){
console.log(`对象删除delete${key}属性`);
return Reflect.deleteProperty(target,key);
}
});
测试:
//get
console.log(objPro.name);
//set+get
objPro.age=1;
console.log(objPro.age);
//has
console.log("height" in objPro);
//delete
delete objPro.name
console.log(objPro);
// 获取getname属性
// kaisa
// age属性发生改变
// age属性设置set成功
// 获取getage属性
// 1
// 判断对象是否有hasheight属性
// true
// 对象删除deletename属性
// Proxy {age: 1, height: 1.88}
用的少,但某些特殊场景会用到。
Reflect.construct(使用哪个类创建, [参数], 创建哪个类的实例)
如:我们有两个构造函数, 一个构造函数是没有对应的操作, 我们可以使用construct借用另一个构造函数创建类。
function Person(name, age) {
this.name = name;
this.age = age;
}
function Student() {}
// 使用Person创建Student的实例
const stu = Reflect.construct(Person, ["kaisa", 18], Student);
// 同时创建的实例的隐式原型, 依然指向Student的显式原型
console.log(stu.__proto__ === Student.prototype); // true
使用receiver参数改变this,让对象所有的操作都经过Proxy代理对象,都能被捕获器捕获。
具体看:Proxy和Reflect中的receiver到底是个什么东西
const obj={
_name:"true_name",
name:"fake",
get name(){
return this._name;
},
set name(newValue){
this._name=newValue;
}
}
const objPro=new Proxy(obj,{
get:(target,key,receiver)=>{
console.log(`get ${key}`);
return Reflect.get(target,key,receiver);
},
set:(target,key,newValue,receiver)=>{
console.log(`set ${key}`);
return Reflect.set(target,key,newValue,receiver);
},
})
console.log(objPro.name);
objPro.name="123";
console.log(objPro.name);
// get name
// get _name
// true_name
// set name
// set _name
// get name
// get _name
// 123
coderwhy的课
Web前端Proxy和Reflect使用详解
Proxy-Reflect
比较 Reflect 和 Object 方法
Proxy和Reflect中的receiver到底是个什么东西