Vue雙向綁定的實現原理系列(一):Object.defineproperty

瞭解Object.defineProperty()

github源碼
html

Object.defineProperty()方法直接在一個對象上定義一個新屬性,或者修改一個已經存在的屬性, 並返回這個對象。

vueJS採用 ES5 提供的 Object.defineProperty() 方法,監控對數據的操做,從而能夠自動觸發數據同步。而且,因爲是在不一樣的數據上觸發同步,能夠精確的將變動發送給綁定的視圖,而不是對全部的數據都執行一次檢測。

首先咱們得先知道,ECMAScript中有兩種屬性:數據屬性和訪問器屬性( ie8如下只能在dom對象上使用;不能使用在普通對象上)vue

數據屬性:
[[Configurable]]: 表示可否修改屬性。默認值爲true

  [[Enumerable]]: 表示屬性是否可枚舉,也就是是否能夠經過for-in循環返回屬性。默認值爲true

  [[Writable]]: 表示可否修改屬性的值。默認值爲true

  [[value]]: 包含這個屬性的值.讀取屬性的時候就是經過這裏開始讀。默認值爲undefined
訪問器屬性:
[[Configurable]]: 表示可否修改屬性。默認值爲true

   [[Enumerable]]: 表示屬性是否可枚舉,也就是是否能夠經過for-in循環返回屬性。默認值爲true

   [[Get]]: 在讀取屬性時調用的函數,默認時undefined

   [[Set]]: 在設置屬性時調用的函數,默認時undefined

    咱們要是想修改默認屬性的值就可使用:Object.defineProperty(obj,prop,descriptor);

1.基本用法:

var a= {}
Object.defineProperty(a,"b",{
    value:123
});
console.log(a.b);//123

2.參數介紹:

第一個參數obj:目標對象a

 第二個參數prop:須要定義的屬性或方法的名字"b"

 第二個參數descriptor:目標屬性所擁有的特性

2.1 第三個參數的取值介紹(descriptor)

value:屬性的值

    writable:若是爲false,屬性的值就不能被重寫,只能爲只讀了

    configurable:總開關,一旦爲false,就不能再設置他的(value,writable,configurable)

    enumerable:是否能在for...in循環中遍歷出來或在Object.keys中列舉出來。

    get:後面介紹

    set:後面介紹

    注意:在 descriptor 中不能同時設置訪問器(get 和 set)和 wriable 或 value,不然會錯,就是說用 get 和 set,就不能用 writable 或 value 中的任何一個

在基本用法裏只設置了value,沒有設置別的,能夠簡單的理解爲(暫時這樣理解)它會默認幫咱們把writable,configurable,enumerable。都設上值,並且值還都是false。(僅限於第一次設置的時候),等同於如下代碼:

    var a = {}; 
    Object.defineProperty(a, 'b', {
         value: 123, 
         writable: false, 
         enumerable: false, 
         configurable: false 
    }); 
    console.log(a.b); //123

2.1.1 configurable介紹

總開關,第一次設置 false 以後,,第二次什麼設置也不行了:
也就是說,你可使用Object.defineProperty()方法無限修改同一個屬性,可是當把configurable改成false以後就有限制了

var a = {};
Object.defineProperty(a, 'b', { 
    configurable: false
}); 
Object.defineProperty(a, 'b',{ 
    configurable: true 
});
//報錯:Uncaught TypeError: Cannot redefine property: b(…)

2.1.2 writable介紹

var a = {}; 
Object.defineProperty(a, 'b', { 
    value: 123,
    writable: false //只讀
});
console.log(a.b); // 打印 123 
a.b = 124; // 沒有錯誤拋出(在嚴格模式下會拋出,即便以前已經有相同的值) 
console.log(a.b); // 打印 123, 賦值不起做用。

2.1.3 enumerable介紹

var a = {}
    Object.defineProperty(a,"b",{
        value:3445,
        enumerable:true
});
console.log(Object.keys(a));// 打印["b"]

//改爲false:

var a = {}
    Object.defineProperty(a,"b",{
        value:3445,
        enumerable:false
});
console.log(Object.keys(a));// 打印[]

2.1.4 set & get

訪問器屬性不能直接定義!只能經過Object.defineProperty()來定義:
var a= {}
Object.defineProperty(a,"b",{
    set:function(newValue){
        console.log("賦值是:"+newValue)
    },
    get:function(){
        console.log("取值:")
        return 2 //注意這裏,我硬編碼返回2
    }
});
a.b =1; //賦值是: 1
console.log(a.b) ;   //取值  2  

簡單來講,這個 b 賦值或者取值的時候會分別觸發 set 和 get 對應的函數

3.Object.defineProperty示例:

//判斷是否是對象
function isObj(obj){
    var type = Object.prototype.toString.call(obj);
    return type === '[object Object]';
}
 
//執行函數:
function objFun(obj){
    if(isObj(obj)){
        new Observer(obj);
    }
}

function Observer(obj){
    this.data = obj;
    this.walk(obj);
}

 //監聽事件函數:
Observer.prototype.walk = function(obj){
    for(var k in obj){
        def(obj,k,obj[k])
    }
}

function def(obj,k,val){
    Object.defineProperty(obj,k,{
        configurable:true,
        enumerable:true,
        get:function(){
            console.log('get取值');
            return val;
        },
        set:function(newVal){
            if(val === newVal){
                return;
            }
            val = newVal;
            console.log('set設置值')
        }
    });
}

//測試:
var obj = {a:111,b:222};
objFun(obj);
console.log(obj.a)//get取值 222
obj.a = 333;//set設置值
console.log(obj)

4.Object.defineProperty實現數據和視圖的聯動:

html:

<div>
    Object.defineProperty實現數據和視圖的聯動: <br>
    <span id="nickName"></span>
    <div id="introduce"></div>
</div>

js:(視圖控制器)

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
    }
});
//console.log(userInfo)
userInfo.nickName = '我是nickName';
userInfo.introduce = '我是introduce'

上面設置userInfo的nickName屬性時會調用set方法,更新DOM節點的HTML

系列文章的目錄:

Vue雙向綁定的實現原理系列(一):Object.defineproperty
Vue雙向綁定的實現原理系列(二):設計模式
Vue雙向綁定的實現原理系列(三):監聽器Observer和訂閱者Watcher
Vue雙向綁定的實現原理系列(四):補充指令解析器compilegit

相關文章
相關標籤/搜索