返回动态
3 分钟阅读

Object.defineProperty妙用——Vue2数据双向绑定的原理

Object.defineProperty方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象,这也是Vue2数据双向绑定的原理

最近看了一下 Vue.js 实现相关的文章,了解到了其数据劫持(双向绑定)的原理,使用到了Object.defineProperty这个方法,花了点时间,自己尝试着做了一个小 demo。 MDN 解释:Object.defineProperty方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

// 使用Object.defineProperty()
// 接收的第一个参数为对象,第二个参数为属性名,第三个参数为配置对象
let obj = {}
Object.defineProperty(obj, 'name', {
  enumerable: true, // 是否可枚举,默认值 false,如果为false,在for in遍历里不会展示属性
  writable: true, // 是否可写,默认值 false,如果为false的话,给name赋值,不会生效
  configurable: true, // 是否可配置(是否可删除),默认值 false
  value: 'huqing' // name对应的值
})

// 上面的写法其实和下面的写法是一样的
let obj = {}
obj.name = 'huqing'

既然写法一样,为何要大费周折去写那么多呢,其实重点在其getset方法

// 注意!当使用get set时,不能写value和writable
let obj = {}
let str
Object.defineProperty(obj, 'name', {
  enumerable: true,
  configurable: true,
  get() {
    // 读,当我们读取属性时,会执行get方法
    return str // 当我们obj.name进行读取时,会返回'huqing'
  },
  set(newValue) {
    // 写,当我们写入属性时,会执行set方法
    str = newValue
  }
})

obj.name = 'huqing' // 写入
console.log(obj.name) // 'huqing'  // 读取

从这可以看出,我们可以在 get、set 函数中,写出对应的业务逻辑,做一系列我们想做的事情

话不多说,开始做 demo

<!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" value="">
  <script>
    let el = document.getElementById('input')
    let obj = { // 1. 创建一个对象
      name: ""
    }

    function oberseve(obj) { // 2. 对对象进行观察
      if (typeof obj !== 'object') return // 2.1 判断参数是否为对象
      for (let key in obj) { // 2.2 对对象进行遍历,目的是为了把每个属性都设置get/set
        defineReactive(obj, key, obj[key])
        if (typeof obj[key] === 'object') {
          oberseve(obj[key]) // 2.3 obj[key]有可能还是一个对象,需要递归,给它的子属性也进行设置
        }
      }
    }

    function defineReactive(target, property, value) { // 3. 使用Object.defineProperty
      Object.defineProperty(target, property, {
        get () {
          el.value = value // 3.1 当读取时,把值赋值给input框
          return value
        },
        set (newVal) {
          el.value = newVal // 3.2 当设置时,把赋值给input框
          value = newVal
        }
      })
    }

    oberseve(obj) // 4.执行该函数,对obj对象里的属性进行设置get/set
    el.addEventListener('input', function () { // 5.给输入框绑定input事件
      obj.name = this.value // 6.当输入框输入内容时,会把输入框的内容赋值给obj.name,触发obj.name的set方法
    })
  </script>
</body>
</html>

当我们在输入框输入之后,在控制台输入 *obj.name *,会发现值已经与输入框里的值一样

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

image

这样就完成了一个简单的数据双向绑定了!

评论