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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

karshey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值