Object.defineProperty用法

三个参数(均为必填)

  1. obj:需要定义属性的对象(目标对象)
  2. prop:需要被定义或修改的属性名(对象上的属性或者方法)
  3. descriptor:需被定义或修改的属性的描述符
// Object.defineProperty(obj, prop, descriptor)
var obj = {};
Object.defineProperty(obj, 'a', {
    value: 2,
    writable: true,
    configurable: true,
    enumerable: true
});
obj.a; // 2

descriptor参数详解

value:属性的值

var obj = {};
Object.defineProperty(obj, 'a', {
    value: 2
})
console.log(obj.a); // 2

writable:属性的值是否能被修改

var obj = {};
Object.defineProperty(obj, 'a', {
    value: 2,
    writable: false, // 不可写!
    configurable: true,
    enumerable: true
})
console.log(obj.a); // 2
obj.a = 3;
console.log(obj.a); // 2

configurable:是否能够配置value, writable, enumerable

var obj = {};
Object.defineProperty(obj, 'a', {
    value: 2,
    writable: true,
    configurable: false, // 不可配置!
    enumerable: true
})
console.log(obj.a); // 2
Object.defineProperty(obj, 'a', {
    value: 3,
    writable: true,
    configurable: true,
    enumerable: true
})
// TypeError: Cannot redefine property: a

enumerable:属性是否能在for…in或者Object.keys中被枚举出来

var obj = {};
Object.defineProperty(obj, 'a', {
    value: 2,
    writable: true,
    configurable: true,
    enumerable: false // 不可枚举!
})
console.log(Object.keys(obj)); // []
Object.defineProperty(obj, 'a', {
    value: 2,
    writable: true,
    configurable: true,
    enumerable: true
})
console.log(Object.keys(obj)); // ['a']

get/set

对于set和get,我的理解是它们是一对勾子函数,当你对一个对象的某个属性赋值时,则会自动调用相应的set函数;而当获取属性时,则调用get函数。这也是实现双向数据绑定的关键。

var obj = {}
var a

Object.defineProperty(obj, 'a', {
    get: function() {
        console.log('get x')

        // 我们可以在这里对返回的值做任何操作
        return x + 1
    },
    set: function(newValue) {
        console.log('set x to', newValue)
        x = newValue
    }
})

obj.a = 100

console.log(obj.a);

/*
output:
  set x to 100
  get x
  101
*/

注意

数据描述符和存取描述符不能混合使用

Object.defineProperty(o, "conflict", {
  // value是数据描述符
  value: 1,
  // get是存取描述符
  get: function() {
    return 2;
  }
});

视图和数据变化绑定

<div>
    <p>你好,<span id='nickName'></span></p>
    <div id="introduce"></div>
</div>
//视图控制器
var userInfo = {};
Object.defineProperty(userInfo, "nickName", {
    get: function(){
        return document.getElementById('nickName').innerHTML;
    },
    set: function(nick){
        document.getElementById('nickName').innerHTML = nick;
    }
});
Object.defineProperty(userInfo, "introduce", {
    get: function(){
        return document.getElementById('introduce').innerHTML;
    },
    set: function(introduce){
        document.getElementById('introduce').innerHTML = introduce;
    }
})
userInfo.nickName = "xxx";
userInfo.introduce = "我是xxx,我来自云南,..."

vue数据变动

但是,这个例子只是数据和dom节点的绑定,而vue.js更为复杂一点,它在网页dom和accessor之间会有两层,一层是Wacher,一层是Directive,比如以下代码。

var a = { b: 1 }
var vm = new Vue({ 
  data: data
})

把一个普通对象(a={b:1})传给 Vue 实例作为它的 data 选项,Vue.js 将遍历它的属性,用Object.defineProperty 将它们转为 getter/setter,如图绿色的部分所示。
每次用户更改data里的数据的时候,比如a.b =1,setter就会重新通知Watcher进行变动,Watcher再通知Directive对dom节点进行更改。