玩转ES6(二)-Object.defineProperty和Proxy代理

简介: 玩转ES6(二)-Object.defineProperty和Proxy代理

Object.defineProperty


Object.defineProperty这个并不是es6的语法,这个是给一个对象,添加属性,但是目前框架很多实用这个方法,来实现数据劫持,也就是数据双向绑定

// 平时我们这样给一个对象添加属性
let obj = {str:"hello swr"}
obj.str = 'goodbye swr'
console.log(obj.str) // 'goodbye swr'

那么当我们想在给一个对象,读取值或写入值时,进行别的操作,该怎么做呢?

// 使用Object.defineProperty()
// 接收的第一个参数为对象,第二个参数为属性名,第三个参数为配置对象
let obj = {}
Object.defineProperty(obj,'name',{
    enumerable:true,// 是否可枚举,默认值 true
                    // 如果为false的话,打印这个obj对象,是看不到name这个属性
    writable:true,  // 是否可写,默认值 true
                    // 如果为false的话,给name赋值,不会生效
    configurable:true, // 是否可配置(是否可删除),默认值 true
                       // 如果为true,delete obj.name,再打印obj,则显示{}
                       // 如果为false,delete obj.name,再打印obj,则显示{name:undefined}
   value:'swr', // name对应的值
})
// 上面的写法其实和下面的写法是一样的
let obj = {}
obj.name = 'swr'

那么既然一样,我们有必要写这么大串的代码吗?

其实核心是get和set,我们继续往下看

// 需要注意的是,当使用get set时,则不能使用value和writable
let obj = {}
let str
Object.defineProperty(obj,'name',{
    enumerable:true,
    configurable:true, 
    get(){ // 读,当我们读取时,则会执行到get,比如obj.name
        // return 'swr' // 当我们obj.name进行读取时,会返回'swr'
        return str
    },
    set(newValue){ // 写,当我们写入时,则会执行到set,比如obj.name = 'swr'
                   // 并且会把newValue作为参数传进去
        str = newValue
    }
})
obj.name = 'swr' // 写入
console.log(obj.name) // 'swr'  // 读取

这样一来,我们可以在get set函数中,写出对应的业务逻辑,

包括很多框架底层,例如

// 一般不再选择这样的写法
Fn.prototype.xxx = xxx
// 更多的是选择这样的写法
// 这样的好处就是当读取值的时候,可以做一系列我们想做的事情
Object.defineProperty(Fn.prototype,'xxx',{...})


那么我们实现数据双向绑定呢?


这个问题在面试当中,会经常问这个问题,但是面试官更希望听到的是具体底层的实现方式,那么接下来我们也实现一下吧~ ( 简陋版的……(#^.^#)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>对象的数据双向绑定</title>
</head>
<body>
  <input id='input' type="" name="" value="">
  <script>
    let el = document.getElementById('input') // 1. 获取输入框的dom节点
    let obj = { // 2. 创建一个对象
      name: ""
    }
    function oberseve(obj) { // 3. 对对象进行观察
      if (typeof obj !== 'object') return // 3.1 判断参数是否为对象
      for (let key in obj) { // 3.2 对对象进行遍历,目的是为了把每个属性都设置get/set
        defineReactive(obj, key, obj[key])
        oberseve(obj[key]) // 3.3 obj[key] 有可能还是一个函数,需要递归,给obj[key]里的属性进行设置get/set
      }
    }
    function defineReactive(target, property, value) { // 4. 使用Object.defineProperty
      Object.defineProperty(target, property, {
        get() {
          el.value = value // 4.1 当读取时,把值赋值给input框
          return value
        },
        set(newVal) {
          el.value = newVal // 4.1 当设置时,把赋值给input框
          value = newVal
        }
      })
    }
    oberseve(obj) // 5.执行该函数,对obj对象里的属性进行设置get/set
    el.addEventListener('input', function () { // 6.给输入框绑定input事件
      obj.name = this.value // 7.当输入框输入内容时,我们会把输入框的
                            //   内容赋值给obj.name,触发obj.name的set方法
    })
  </script>
</body>
</html>

当我们在输入框输入内容时,再到控制台输入obj.name查看这个值时,会发现打印出"hello swr"

08bb4d1aa6f8f02a2e3ad4da2d8b1244_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

当我们在控制台,给obj.name赋值时,会发现输入框的内容也会作出相应更改

e77481a201adbf6ef52f17b6bde4fa3d_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.jpg

这样我们就实现了一个简陋版的数据双向绑定了,但是这也是有缺点的,这个只是针对对象进行了数据双向绑定,而尤大大的Vuejs就是基于Object.defineProperty实现的。

除了Object.defineProperty可以实现数据双向绑定之外,还有其他方式吗?

肯定是有其他方式可以实现的,利用es6的proxy代理也可以实现数据双向绑定,但是目前的框架还是比较少使用这种方式。


Proxy


Proxy代理也可以进行数据劫持,但是和Object.defineProperty不同的是,Proxy是在数据外层套了个壳,然后通过这层壳访问内部的数据,目前Proxy支持13种方式。

9b8f1e38d40128420ea7ca13b7b4e77f_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.jpg

Proxy,我的理解是在数据外层套了个壳,然后通过这层壳访问内部的数据,就像下面的图

be5641d3fcb10883dba5a6298ffeb88b_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.jpg

let dog = {
  name:"小黄",
  firends:[{
    name:"小红"
  }]
}
// 1.首先new一个Proxy对象
let proxy = new Proxy(dog,{ // 2.参数一为需要代理的数据,参数二为上图可以代理的13种的配置对象
    get(target,property){ // 3.参数1为上面dog对象,参数2为dog的属性
        console.log('get被监控到了')
        return target[property]
    },
    set(target,property,value){ // 4.参数1为上面dog对象,参数2为dog的属性,参数3为设置的新值
                                // 有点类似Object.defineProperty
        console.log('set被监控到了')
        target[property] = value
    }
})
// 那么接下来我们设置一下这个属性
// dog.name = '小红'  // set值时,发现不会打印 'set被监控到了'
// dog.name // get值时,发现不会打印 'get被监控到了'
// 思考:为什么在set/get值的时候不会打印出来我们需要的东西呢?
// 上面说得很明白了,proxy相当于是一个壳,代理我们需要监控的数据,也就是我们要通过proxy来访问内部数据才会被监控到
proxy.name = '小红' // 打印输出 'set被监控到了'
proxy.name // 打印输出 'get被监控到了'
// Reflect经常和Proxy搭配使用
// 比如我们上面的例子中
let proxy = new Proxy(dog,{ 
    get(target,property){ 
        console.log('get被监控到了')
        return target[property]
    },
    set(target,property,value){ 
        console.log('set被监控到了')
        // target[property] = value 
        // 这里的target[property] = value 可以用下面的写法
        Reflect.set(target,property,value)
    }
})
// 那么我们该怎样实现深度的数据劫持呢?
let dog = {
  name:"小黄",
  firend:{
    name:"小红"
  }
}
// 我们首先写一个set方法,希望是通过这样来调用
set(dog.firend,funtion(obj){
    console.log(obj) // { name:"小红" }  回调函数中的obj代表的是dog.firend的对象
})
// 实现
let dog = {
  name:"小黄",
  firend:{
    name:"小红"
  }
}
function set(obj,callback){
    let proxy = new Proxy(obj,{
        set(target,property,value){
            target[property] = value
        }
    })
    // 最后把proxy传给我们的回调函数
    callback(proxy)
}
set(dog.firend,function(obj){
    console.log(obj) // { name:"小红" } 实际就是从set函数中传出来的proxy对象
})

目录
相关文章
|
7月前
|
JavaScript 前端开发
【面试题】 JS手写ES6的Object.create方法
【面试题】 JS手写ES6的Object.create方法
|
3月前
ES6中map对象的使用,确实比Object好使哈
ES6中Map对象的使用优势,包括任意类型作为键、直接获取大小、增删查改操作等。Map的键可以是函数、对象、NaN等,支持forEach循环和for...of循环。
37 1
ES6中map对象的使用,确实比Object好使哈
|
2月前
|
JavaScript 前端开发 UED
为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty
Vue 3.0 采用 Proxy 替代 Object.defineProperty,主要因为 Proxy 提供了更全面、高效的数据拦截能力,支持对更多操作进行拦截和自定义处理,同时减少了对对象的限制,提升了框架性能和开发体验。
|
4月前
|
JavaScript 前端开发 开发者
Vue.js 响应式变革来袭!结合热点技术,探索从 Object.defineProperty 到 Proxy 的奇妙之旅,触动你的心
【8月更文挑战第30天】在 Vue.js 中,响应式系统自动追踪并更新数据变化,极大提升了开发体验。早期通过 `Object.defineProperty` 实现,但存在对新旧属性处理及数组操作的局限。Vue 3.0 引入了 `Proxy`,克服了上述限制,提供了更强大的功能和更好的性能。实践中,可根据项目需求选择合适的技术方案,并优化数据操作,利用懒加载等方式提升性能。
48 0
|
JavaScript 前端开发 API
proxy相对于object.defineproperty有哪些优点?
proxy相对于object.defineproperty有哪些优点?
|
7月前
|
缓存 前端开发 JavaScript
理解 Proxy 和 Object.defineProperty:提升你的 JavaScript 技能(下)
理解 Proxy 和 Object.defineProperty:提升你的 JavaScript 技能(下)
理解 Proxy 和 Object.defineProperty:提升你的 JavaScript 技能(下)
|
7月前
|
JavaScript 前端开发 测试技术
理解 Proxy 和 Object.defineProperty:提升你的 JavaScript 技能(上)
理解 Proxy 和 Object.defineProperty:提升你的 JavaScript 技能(上)
理解 Proxy 和 Object.defineProperty:提升你的 JavaScript 技能(上)
|
7月前
|
JavaScript
【Object.defineProperty() | new Proxy()】操作Object
【Object.defineProperty() | new Proxy()】操作Object
|
7月前
|
JavaScript 前端开发 测试技术
Proxy vs Object.defineProperty:哪种对象拦截机制更适合你?
Proxy vs Object.defineProperty:哪种对象拦截机制更适合你?
|
7月前
|
JavaScript 前端开发 数据安全/隐私保护
Proxy 与 Object.defineProperty 优劣对比
Proxy 与 Object.defineProperty 优劣对比
168 0