vue源码解读之mvvm实现原理之Object.defineProperty

整体需要三个部分

  • 实现Observer
  • 实现Compile
  • 实现Watcher

Observer:需要利用Object.defineProperty,将要观察的对象,转化成getter/setter,以便拦截对象赋值与取值操作,称之为Observer

简单理解就是,通过Object的defineProperty,监听其内部所有的属性变化。

下面缕一缕思路创建一个Observer

开头这部分是我找来的定义,细看戳这里。

Object.defineProperty(obj, prop, descriptor)中的descriptor

descriptor:该方法允许精确添加或修改对象的属性。通过赋值来添加的普通属性会创建在属性枚举期间显示的属性(for...in 或 Object.keys 方法), 这些值可以被改变,也可以被删除。

对象里目前存在的属性描述符有两种主要形式: 数据描述符存取描述符

  • 数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。
  • 访问器描述符是由getter-setter函数对描述的属性。

描述符必须是这两种形式之一;不能同时是两者。

共有属性

  • configurable当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
  • enumerable当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。

数据描述符同时具有以下可选键值:

  • value该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
  • writable当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。

存取描述符同时具有以下可选键值:

  • get一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为 undefined。
  • set一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined。
  • configurable当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
  • enumerable当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。

接下来进行一下测试

数据描述符 属性demo

 /*************************************************
 * 数据描述符 属性demo
 */
// 数据描述符
var obj = {}
Object.defineProperty(obj, 'a', {
    configurable: true,
    enumerable: true,
    writable: true,
    value: 123
})
Object.defineProperty(obj, 'b', {
    configurable: true,
    enumerable: false,
    writable: true,
    value: 456
})
Object.defineProperty(obj, 'c', {
    configurable: true,
    enumerable: true,
    writable: false,
    value: 789
})

Object.a = '小明'
Object.c = '小白'
console.log(obj)    // {a: 123, b: 456, c: 789}

for (var item in obj) {
    console.log(item)   // a c
}

这里configurable和enumerable为共有属性,当enumerable为false时,for in 循环是遍历不到的。writable控制是否可修改,当writable是false时,修改无效。

存取描述符 属性demo

/*************************************************
 * 存取描述符 属性demo
 */
var obj = {},
    myData = null
Object.defineProperty(obj, 'myData', {
    set: function (value) {
        myData = value
        console.log('触发更新')
    },
    get: function () {
        return myData + ' ->get'
    }
})
obj.myData = '123'  // 触发更新
obj.myData = '456'  // 触发更新
console.log(obj)    // {myData:"456 ->get"}

这里在set中添加console.log指令,每次数据修改都会触发。这也是vue实现数据拦截的基础。

同样的,在获取对象时,get触发的是get方法。

随机浏览