object.definedProperty 和 proxy

本文详细探讨了JavaScript中object.defineProperty的局限性和proxy的使用,比较了两者在属性监听和操作拦截方面的特性,并介绍了Reflect对象在代理中的应用,展示了如何通过proxy实现更灵活的属性管理和操作监控,以及Proxy对象的捕获器和Reflect方法的对应关系。
摘要由CSDN通过智能技术生成

object.definedProperty 和 proxy

object.defineProperty

  1. 缺点
    • 定义属性的时候,定义的是普通的属性,但是后面将其强行转换为了数据属性操作符
    • Object.defineProperty 无法监听新增属性,删除属性。
    • 一次只能对一个属性监视,要是想对所有属性监听,需要递归监听
    • 对于数组的push, unshift方法增加的元素,也无法监听
  2. 示例
  • 监听单个属性
const obj = {
    name: "hello",
    age: 18,
    height: 1.88
}
let _name = obj.name
Object.defineProperty(obj, "name", {
    set: function(newValue) {
        console.log(newValue)
        _name = newValue
    },
    get: function() {
        console.log(_name)
        return _name
    }
})
  • 监听对象上的多个属性
const keys = Object.keys(obj)
for (const key of keys) {
    let value = obj[key]
    Object.defineProperty(obj, key, {
        set: function(newValue) {
        
        },
        get: function() {
        
        }
    })
}
  • 弊端
Object.keys(person).forEach(function, key) {
    Object.defineProperty(person, key, {
        enumerable: true,
        configurable: true
    })
    get() {
        return person[key]
    },
    set(val) {
        person[key] = val
        console.log(person[key])
    }
}

上述代码中,会造成栈溢出,不断触发get属性
设置中转函数,就不会造成栈溢出,但是依然不能监听很多其他的属性

let person = {
    name: '',
    age: 0
}
function defineProperty(obj, key, val) {
    Object.defineProperty(obj, key, {
        get() {
            console.log(`访问了${key}属性`)
            return val
        },
        set(newVal) {
            console.log(`${key}属性被修改为${newVal}`)
            val = newVal
        }
    })
}
function Observer(obj) {
    Object.keys(obj).forEach((key) => {
        definePRoperty(obj, key, obj[key])
    })    
}
Observer(person)
  • 深度监听对象
function defineProperty(ojb, key, val) {
    if(typeof val === 'object') {
        observer(val)
    }
    Object.defineProperty(obj, key, {
        get() {
            console.log(key)
            return val
        },
        set(newValue) {
            val = newValue
            console.log(val)
        }
    })
}
function Observer(obj) {
    if (typeof obj !== "Object" || obj === null) {
        return 
    }
    Object.keys(obj).forEach((key) => {
        defineProperty(obj, key, obj[key])
    })
}
//如果set传入的是一个对象,
set(newVal) {
    if(typeof newVal === 'object'){
        observer(key)
    }
    console.log(`${key}属性被修改为${newVal}`)
    val = newVal
}

proxy

  • 监听一个对象的相关操作,可以先创建代理对象proxy对象,之后改对象的所有操作,都可以通过代理对象来完成,代理对象可以监听对源对象进行的操作.在目标对象之前架设了一层拦截,外界对该对象的访问,都必须先通过这层连接,可以对外界的访问进行过滤和改写
  1. 语法

    • const p = new Proxy(target, handler)
      target: 要使用proxy包装的目标对象,可以是任何类型的对象,包括原生数组,函数甚至是另一个代理
      handler: 通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理p的行为。如果handler没有设置任何拦截,就等同于通向原对象
  2. 示例

        const obj = {
            name: '不知道啊'
        }
        const objProxy = 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(objProxy.name)
        objProxy.name = "koko"
        console.log(objProxy.name)
    
    let subject = ['高数']
    let handler = {
        get(obj, key) {
            console.log('触发了get')
            return key in obj?obj[key]: '没有这门学科'
        },
        set(obj, key, val) {
            console.log('触发了set')
            obj[key]  = val
            return true
        }
    }
    let ProxyObj = new Proxy(subject, handler) 
    console.log(ProxyObj)
    console.log(ProxyObj[1])
    ProxyObj[0] = '大学物理'
    console.log(ProxyObj[0])
    ProxyObj.push('线性代数') //调用了两次set和get方法
    console.log(ProxyObj[1])
    
  3. Getter Setter

    • set函数四个参数: target property value receiver
      • target: 目标对象
      • property: 讲被设置的属性key
      • value: 新属性值
      • receiver: 调用的代理对象
    • get函数的三个参数
      • target : 目标对象 侦听的对象
      • property: 被获取的属性key
      • receiver: 调用的代理对象
    • 示例
      function foo(num1, num2) {
      console.log(this, num1, num2)
      }
      const fooProxy = new Proxy(foo, {
          apply: function(target, thisArg, otherArgs) {
              console.log("监听执行了apply操作");
              target.apply(thisArg, otherArgs)
          },
          construct: function(target, otherArray) {
              console.log("监听了new操作");
              console.log(target, otherArray)
              return new target(...otherArray)
          }
      })
      new fooProxy("aaa", "bbb")
      
  4. proxy 的所有捕获器

    • getPrototypeOf
    • setPrototypeOf
    • isExtensible()
    • preventExtensions
    • getOwnPropertyDescriptor
    • defineProperty
    • ownKeys
    • has
    • get
    • set
    • deleteProperty
    • apply
    • construct

reflect

  • 是一个内置的对象,提供了JavaScript操作的方法,reflect不是一个函数对象,因此是不可构造和的,reflect所有的方法都是静态的
  • 将object对象的一些明显属于语言内部的方法,放到reflect对象上,现阶段,某些方法同时在Object和Reflect。未来的方法会部署到Reflect对象上
  • 修改某些object方法的返回结果,结果会变得更加合理,会返回true 或者flase
  • 让Object操作都变成函数行为,某些object操作是命令式,可以将他们变成函数行为
  • reflect对象的方法和proxy对象的方法一一对应,只要是proxy对象上的方法,都能在reflect上找到对应的方法,这就让proxy对象可以方便的调用对应的reflect方法,完成默认行为,作为修改默认行为的基础
  1. 示例
    • set函数四个参数: target property value receiver
      • target: 目标对象
      • property: 讲被设置的属性key
      • value: 新属性值
      • receiver: 调用的代理对象
    let obj = {
        _name: "why",
        set name(newValue) {
            console.log("this", this)
            this._name = newValue
        },
        get name() {
            return this._name
        }
    }
    const objProxy = new Proxy(obj, {
        set:function(target, key, newValue, receiver) {
            console.log(target)
            console.log(key)
            console.log(newValue)
            console.log(receiver)
            const isSuccess = Reflect.set(target, key, newValue,receiver)
            if(!isSuccess) {
                throw new Error(`set ${key} failure`)
            }
        },
        get: function(target, key, receiver) {
    
        }
    })
    objProxy.name = 'lisi'
    console.log(objProxy.name)
    
    • 这里我尝试对为什么要在这里使用reflect进行一下说明
      • 不使用reflect的情况:设置objProxy.name = 'lisi' ,被objProxy代理拦截,这种情况下,代码应该是这样的
        set:function(target, key, newValue, receiver) {
            target[key] = newValue
        }
        
        这里的target是指的是目标对象,key是键,将目标键设置为对应的值,设置的是obj对象,在obj对象中,会存在set方法,对键值进行设置,会触发set方法,输出this的值,这里的this是obj。然后再执行到这行代码this._name = newValue的时候,还是会对obj中的name属性进行设置,又会触发obj对象里面的set方法
      • 使用reflect的情况:设置objproxy.name = 'lisi' ,被objProxy代理拦截,这种情况下,代码如上.通过反射调用obj的set方法,传入对象,键值,新值,以及proxy对象。再obj中,执行set方法, 这里的this指的是proxy方法,将proxy方法中的name进行赋值,再这个过程中,还会执行proxy中的set方法。
      • 总而言之,不设置reflect,不传入receiver的情况下,this指向的是obj,当传入proxy的时候,这个this值得是proxy。
  2. 参数解释
    • receiver参数
      如果源对象obj中有setter getter的访问器属性的时候,可以通过receiver来改变里面的this
      const objProxy = new Proxy(obj, {
      has: function(target, key) {
          return Reflect.has(target, key)
      },
      set: function(target,key, value, receiver) {
          console.log("set捕获器",key)
          return Reflect.set(target, key, value,receiver)
      },
      get: function(target, key, receiver) {
          console.log("get捕获器", key)
          return Reflect.get(target, key,receiver)
      },
      deleteProperty: function(target, key) {
          return Reflect.deleteProperty(target, key)
      }
      })
      
    • construct
      可以不用new创建一个对象,第一个参数必须是函数,否则会出错
      Reflect.construct(target, argumentsList[, newTarget])
      function Student(name, age) {
          this.name = name
          this.age = age
      }
      function Animal() {
      }
      const stu = Reflect.construct(Student, ['hello',"world"], Animal)
      
      这里的Animal调用了student的apply方法。但是这里还是和apply方法创建的函数有一点不同,apply创建的对象construtc指向的是undefiend, 而reflect.construct 创建的对象指向的是target。
      function Person(name, age) {
          this.name = name
          this.age = age
      }
      function Student(name, age) {
          const _this = Reflect.construct(Person, [name, age], Student)
          return _this
      }
      const stu = new Student("why", 18)
      console.log(stu)
      console.log(stu.__proto__ === Student.prototype)
      
  • 28
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值