使用new Proxy,自定义object的setter 和getter,Proxy 和 Object.defineProperty 的区别,Reflect 对象是啥

 每日鸡汤,每个你想要学习的念头都是未来的你向自己求救

前言

这个适用于一个单独的js或者ts 文件,如果在vue文件里面你完全可以用vue的 computed 中的getter和setter, 可以在setter写你想要执行的操作。

setter和getter到底是个啥

(1)set 意为 设置,所以setter 可以理解为设置值的方法,既然是设置值,肯定参数至少有一个【新值】;

(2)get 意为 获取,所以getter 可以理解为获取值的方法,这个方法可以没有参数;

我们可以为任意一个对象设置setter 和getter

const obj = {
    name: 'a',
    // set和get的方法名可以相同也可以不同
    set changeName(newVal) { 
        console.log('setter调用')
        this.name = newVal
    },
    get getName() {
        console.log('getter 调用')
        return this.name
    }
}   
obj.name = 'new val'
console.log(obj)

使用场景

项目: vue2项目

需求:有一个对象

obj = {
    name: 'csdn',
    age: '110'
}

我在修改对象obj.name 之后,需要调用afterChangeName方法,来执行一系列A操作、

在修改对象obj.age之后,需要调用afterChangeAge方法 ,来执行一系列B操作

而且修改两个字段的频率比较高,假设我们直接写会是这样:

const obj = {
    name: 'csdn',
    age: 12
}

function afterChangeName() {
   // 一系列A操作
}
function afterChangeAge() {
   // 一系列B操作
}

// 入口方法
function main() {
    // ...一系列操作
    obj.name  = 'new name'
    afterChangeName()
    
    // 又...一系列操作
    obj.name  = 'new name'
    afterChangeName()

     // 又...一系列操作
    obj.name  = 'new name'
    afterChangeName()

}

在每次修改name 的时候都需要执行 afterChangeName方法,写好多遍,很麻烦,我们当然可以把修改 obj.name 的逻辑放在 afterChangeName 方法中,但是那么整个功能的逻辑就不是很清晰了。

所以我们可以使用代理 proxy ,代理整个对象,就是监控到对象 obj 的属性 name 变化就执行afterChangeName 操作,给人一种自动执行的感觉,很好用。

代理之后,在应用的时候,只需要修改 obj.name 就行了,不用手动调用afterChangeName方法了,这也是 vue3 响应式实现的原理。

一、使用 proxy 代理一个对象

代理的本质是对一个对象的代理,Proxy 是一个构造函数,通过 Proxy 创建一个对象的代理,从而实现基本操作【增删改查】的拦截和自定义。

new Proxy 第一个参数 target 是对象,第二个参数是一个以函数作为属性的对象,【注意第二个参数也是对象】,使用代理可以拦截并修改目标对象上的操作。使用代理之后,就需要使用 proxy 返回的实例来操作对象才能实现功能

Proxy 的使用场景包括但不限于:

  1. 数据劫持和监听: 通过拦截属性访问和赋值操作,可以实现对对象属性的监听、修改和验证,从而实现数据的双向绑定、响应式更新等功能,常用于 Vue.js、React 等前端框架中的状态管理。
  2. 对象验证和安全控制: 通过拦截属性的访问和赋值操作,可以对对象属性的读取和写入进行验证和安全控制,防止非法操作和数据泄露。
  3. 缓存和优化: 通过拦截函数调用操作,可以实现对函数的参数缓存、结果缓存等优化操作,提高程序的性能和效率。
  4. 代理对象扩展: 可以利用代理对象实现原有对象的扩展,添加额外的功能或逻辑,而不影响原有对象的结构和行为。
  5. 数据模型和ORM: 可以利用代理对象实现数据模型的封装和管理,实现对数据的增删改查操作,以及数据与数据库之间的映射和同步。
  6. 关于handler拦截属性

上述例子,我们用 proxy 代理 object,在这个 name 设置值,即执行 obj.name = 'csdn'的时候,就会触发代理中的 setter 方法,我们只需要将afterChangeName方法的操作放在setter函数中即可

const obj = new Proxy ({

    name: ''// 这是第一个参数, new proxy的第一个花括号中是一个object,就是下面的target
}, {

    set: (target, property, value, reciver) => {
         // setter中执行各种操作,存储等
         // afterChangeName() 
        return Reflect.set(target, property, value) //todo 这是赋值操作
    },
    
    get: (target, property, reciver) => {
        // 可以这样写
        // if (property === 'name') {
        //     return '更改后的name 的值';    
        // }
        // 建议这样写
        return Reflect.get(traget, property)
    }

})

console.log(obj.name) // 更改后的name 的值

二、Reflect 相关的两个静态方法

Reflect 是 es6 中操作对象而提供的新的api,就是为了操作对象

  1. Reflect 对象不是构造函数
  2. 将 object 对象的一些明显属于语言内部的方法,如 Object.defineProperty 放到 Reflect对象上,现阶段,某些方法同时在 Object 和 Reflect 对象上部署,未来的新方法将只部署在 Reflect 对象上。也就是说,从 Reflect 对象上可以拿到语言内部的方法。
  3. 修改某些 Object 方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而 Reflect.defineProperty(obj, name, desc)则会返回 false。
  4. 让 object 操作都变成函数行为,某些 Object 操作是命令式,比如 name in obj 和 delete obj[name],而 Reflect.has(obj, name)和 Reflect.deleteProperty
  5. 只要 proxy 对象具有的代理方法,Reflect 对象全部具有,以静态方法的形式存在【静态方法就是使用 Reflect.xx 调用】
  6. 使用 Reflect 避免直接操作目标对象,直接操作目标对象可能会破坏 Proxy 的封装性和透明性,从而导致意外的发生,通过 Reflect 对象,可以确保所有的操作都经过了 Proxy 的拦截器,从而保持了 proxy 的封装性和透明性

1. Reflect.set(target, key, value, receiver

receiver:如果遇到 setterreceiver则为setter调用时的this值。

其实就是给一个对象的属性设置值,类似于obj.key = value,就是这么简单,只是人家是函数的形式。你可以继续选择还用普通的对象的设置值的方法。

但是,代理「proxy」作为一个新的语法,给配一个新的方法,最好是配套使用他的新方法,让它骄傲一下,不足为过吧。

而且使用这个方法的好处是什么?他有boolean类型的返回值,可以返回属性的值是否设置成功。

2. Reflect.get(target, key, receiver)

同理,是和Reflect.set配套的获取的方法

3.更多方法

三、proxy 和 Object.defineProperty 的区别

  1. Object.defineProperty
    1. 静态方法,用于定义或修改对象的单个属性
    2. 三个参数obj, key, handler
    3. 不会修改原型链
    4. 针对单个属性
      1. 如果要代理整个对象必须
        1. 必须遍历对象的每个属性
        2. 必须深层遍历签到的对象
    5. 不能监听数组的变化
      1. vue 2使用这个,vue2解决数组监听的方法是将能够改变原数组的方法进行重写
    6. 只能监听直接对对象属性的增加删除操作,不能监听 object.assign 添加属性
      1. 增加/删除操作也需要提前再 defineProperty 里面监听改变量
    7. 兼容性较好 es5引入
  2. proxy
    1. 是个构造函数,两个参数 obj 和 handler
    2. 代理的是整个对象
    3. 代理之后就需要使用 proxy 构造函数返回的实例进行操作了,而不是原来的obj
    4. 会修改原型链
    5. 可以监听数组的变化
    6. 有多种拦截方法,可以直接监听对象的增加和删除
    7. vue3 s使用这种方法
    8. 代理可以监听到对象中的对象引用的访问,但是不能直接拦截到内部对象属性的访问
    9. 如果你想要监听基本数据类型的变化,可以考虑将它们封装在对象中,然后对对象使用代理来监听属性的变化, str.value,这也是为什么vue3中使用 .value

总结

代理是很好用的方法,虽然其实我们用的不是很多,但是理解了代理的使用方法之后,我们再去学习vue的响应式原理,就很容易搞懂了,vue3的响应式原理就是代理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值