JavaScript中MVVM框架是如何实现双向绑定的

我们先来看一个简单的实现思路。

// 定义一个变化通知的回调
var callback = function(newVal, oldVal) {
  alert(newVal + '---' + oldVal)
}
// 定义一个普通对象作为数据模型
var data = {
  a: 10,
  level1: {
    b: 'str',
    c: [1,2,3],
    level2: {
      d: 30
    }
  }
}
// 实例化一个检测对象,去检测数据,并在数据发生改变的时候做出反应
var j = new Jsonob(data, callback)

上面代码中,我们定义了一个 callback 回调函数,以及一个保存着普通 json 对象的变量 data,最后实例化了一个监测对象,对 data 进行变化监测,当变化发生的时候,执行给定的回调进行必要的变化通知,这样,我们通过一些手段就可以达到数据绑定的效果。

Object.defineProperty

ES5 描述了属性的特征,提出对象的每个属性都有特定的描述符,你也可以理解为那是属性的属性。。。

ES5 把属性分成两种,一种是数据属性,一种是访问器属性,我们可以使用 Object.defineProperty() 去定义一个数据属性或访问器属性。如下代码:

var obj = {}
obj.name = 'yyf'

上面的代码我们定义了一个对象,并给这个对象添加了一个属性 name,值为 ‘yyf’,我们也可以使用 Object.defineProperty() 来给对象定义属性,上面的代码等价于:

var obj = {}
Object.defineProperty(obj, 'name', {
  value: 'yyf',  //属性的值
  writable: true,  //是否可写
  enumerable: true,  //是否能够通过for in枚举
  configurable: true  //是否可使用delete删除
})

这样我们就使用 Object.defineProperty 给对象定义了一个属性,这样的属性就是数据属性,我们也可以定义访问器属性:

var obj = {}
Object.defineProperty(obj, 'age', {
  get: function() {
    return 20
  },
  set: function(newVal) {
    this.age += 20
  }
})

访问器属性允许你定义一对 getter/setter ,当你读取属性值的时候底层会调用 get 方法,当你去设置属性值的时候,底层会调用 set 方法。

知道了这个就好办了,我们再回到最初的问题上面,如何检测一个普通对象的变化,我们可以这样做:

遍历对象的属性,把对象的属性都使用 Object.defineProperty 转为 getter/setter ,这样,当我们修改一些值的时候,就会调用 set 方法,然后我们在 set 方法里面,回调通知,不就可以了吗,来看下面的额代码:

// index.js
const OP = Object.prototype
export class Jsonob {
  constructor(obj, callback) {
    if(OP.toString.call(obj) !== '[object Object]') {
      console.error('This parameter must be an object:' + obj)
    }
    this.$callback = callback
    this.observe(obj)
  }
  observe(obj) {
    Object,keys(obj).forEach(function(key,index,keyArray) {
      var val = obj[key]
      Object.defineProperty(obj, key, {
        get: function(){return val},
        set: (function(newVal){
          this.$callback(newVal)
        }).bind(this)
      })
      if(OP.toString.call(obj[key]) === '[object Object]') {
        this.observe(obj[key])
      }
    }, this)
  }
}

上面代码采用 ES6 编写,index.js 文件中导出了一个 Jsonob 类,constructor 构造函数中,我们保证了传入的对象是一个 { } 或 new Object() 生成的对象,接着缓存了回调函数,最后调用了原型下的 observe 方法。

observe() 方法是真正实现监测属性的方法,我们使用 Object.kes(obj).forEach 循环 obj 所有可枚举的属性,使用 Object.defineProperty 将属性转换为访问器属性,然后判断属性的值是否是一个对象,如果是对象的话再进行递归调用,这样一来,我们就能保证一个复杂的普通 json 对象中的属性以及值为对象的属性都转换成访问器属性。

最后,在 Object.defineProperty 的 set 方法中,我们调用了指定的回调,并将新值作为参数进行传递。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值