• 【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到底是个什么东西

  • 相关阅读:
    Jenkins入门——安装docker版的Jenkins & 配置mvn,jdk等 & 使用案例初步 & 遇到的问题及解决
    leetcode 二分查找·系统掌握 寻找旋转排序数组中的最小值II
    在Ubuntu18.04安装适合jdk8的eclipse
    ndk开发之native层访问java层
    6.同步异步、正则表达式
    C及C++标准与QT版本介绍
    【代码随想录】算法训练营 第七天 第三章 哈希表 Part 2
    AQS源码三视-JUC系列
    QEM网格简化算法学习
    程序分析与优化 - 8 寄存器分配
  • 原文地址:https://blog.csdn.net/karshey/article/details/127109091