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的四个参数:

  1. target:目标对象(侦听的对象)
  2. property:将被设置的属性key
  3. value:新属性值
  4. receiver:调用的代理对象

get的三个参数:

  1. target:目标对象(侦听的对象)
  2. property:将被设置的属性key
  3. receiver:调用的代理对象

代码:

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 反射

es6 Reflect 与 Proxy

ES6深入浅出-13 Proxy 与 Reflect-2.Proxy 代理

ES6新特性:Javascript中的Reflect对象