JavaScript高级Proxy和Reflect
Posted karshey
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript高级Proxy和Reflect相关的知识,希望对你有一定的参考价值。
文章目录
存储属性描述符监听对象
用存储属性描述符来监听这个对象中的属性被设置或获取的过程:
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
对象) - 之后对该对象的所有操作,都通过代理对象来完成:代理对象可以监听我们想要对原对象进行哪些操作
我们可以将上面的案例用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
set和get捕获器
如果我们想要侦听某些具体的操作,就可以在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
Proxy的其他捕获器
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捕获器
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
作用
Reflect是ES6新增的一个API,它是一个对象,字面的意思是反射。
Object和Reflect对象之间的API关系:比较 Reflect 和 Object 方法
作用:
- 提供了很多操作javascript对象的方法,有点像Object中操作对象的方法
- 如
Reflect.getPrototypeOf(target)
类似于Object.getPrototypeOf(traget)
为什么要有Reflect:
- 在早期的ECMA规范中没有考虑到这种对 对象本身 的操作如何设计会更加规范,所以将这些API放到了Object上面作为类方法,但Object作为一个构造函数,这些操作实际上放到它身上并不合适
- ES6中新增了
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
用的少,但某些特殊场景会用到。
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
使用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到底是个什么东西
以上是关于JavaScript高级Proxy和Reflect的主要内容,如果未能解决你的问题,请参考以下文章
掌握 JS 高级编程基础 - Reflect Metadata
@芥末的糖 ---------- ES6---Proxy与Reflect 实现重载(overload)
ES6深入浅出-13 Proxy 与 Reflect-1.Reflect 反射