双向绑定之数据监听

1.定义

根据View与Model之间的关系,可以分为单向绑定和双向绑定,本文中的双向绑定的概念为当V改变时,M也跟着改变,当M改变时,V也跟着改变
实现双向绑定,一般我们需要定义Observe,Watch,Compiler,本文为第一部分,讲解怎么去实现一个数据监听

2.Object.defineProperty

如何去实现数据监听,主要的思路就是当获取值或者设置时,我们能知道,Object正好有这个属性,我们来看看定义
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象

Object.defineProperty(obj, prop, descriptor)

2.1 obj
这是第一个参数,要定义的对象
2.2 prop
要定义的属性
2.3 descriptor
该属性具有的特性

  • 2.3.1 configurable
    当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false
    这句话的意思是,假如为false,那么假如已经使用definePropery来定义了,就不能再定义了,或者不能用delete删除,看代码
  var obj = {}
  Object.defineProperty(obj, 'key1', {
    value: 'test',
    configurable: false
  })
  Object.defineProperty(obj, 'key1', {
    value: 'test2',
    writable: false
  })

此时就会报下面的错误,不允许重新定义


image.png

我们把代码改成下面的,再执行删除试试

  var obj = {}
  Object.defineProperty(obj, 'key1', {
    value: 'test',
    configurable: false
  })
  delete obj.key1

此时去打印obj.key1时,仍然能够打印test,假如加上use strict,还会报错

  "use strict"
  var obj = {}
  Object.defineProperty(obj, 'key1', {
    value: 'test',
    configurable: false
  })
  delete obj.key1
  console.log(obj.key1)
image.png
  • 2.3.2 enumerable
    代表该属性是否可枚举,关于属性的枚举属性,请参考这里,一个属性是否能被枚举,本文用的Object.keys,来看代码
  var obj = {}
  Object.defineProperty(obj, 'key1', {
    value: 'test',
    enumerable: true
  })
  console.log(Object.keys(obj))

此时,打印的结果如下


image.png

假如把enumerable改为false,结果如下


image.png
  • 2.3.3 value
    该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined,这里要注意的是,假如定义了value,就不能定义set或者get
  • 2.3.4 writable
    代表该值是否可写
  "use strict"
  var obj = {}
  Object.defineProperty(obj, 'key1', {
    value: 'test',
    writable: false
  })

此时修改key1是无用的,但是不会报错


image.png
  • 2.3.5 set get
    这两个属性用来一起说明吧,看官网的定义有点生涩,我们用来理解成,当读取这个属性时,调用get,给这个属性设置值时,调用set,这是不是已经说出了我们定义观察者的基本思路了,接下来我们就根据Object的这个属性来定义我们的Observe

3.Observe

我们怎么去实现一个观察者呢,基本的思路就是,每个属性我们都用defineProperty,设置set和get,那么在读取属性和设置属性时,我们都能监听到。根据这个思路,使用递归是肯定的了,我们一层层地去遍历对象,直到没有子对象为止。
思路有了, 我们来看代码吧

function Observe(obj) {
    if (!obj || Object.prototype.toString.call(obj).toLowerCase() != '[object object]') {
      return
    }
    Object.keys(obj).forEach((item) => {
      // 取值,并且把这个值存起来,要是在defineProperty后再取值,会陷入死循环
      var value = obj[item]
      Object.defineProperty(obj, item, {
        set: (newValue) => {
          console.log('set value')
          if (value == newValue) {
            return value
          } else {
            value = newValue
            return newValue
          }


        },
        get: () => {
          console.log(`get key:${item},value:${value}`)
          return value
        }
      })
      //  继续去给当前的这个对象进行遍历
      return Observe(value)
    })
  }

上面的代码,是不是很简单,不超过20行就实现了一个观察者,在本文中,我们仅仅用到了set和get方法,去除Object.defineProperty的理论知识,干货真的不多,但却实现了我们双向绑定的最核心的部分

你可能感兴趣的:(双向绑定之数据监听)