其實vue.js作者在這里的時候聊到了原理,也就是關(guān)于視圖和數(shù)據(jù)動態(tài)變化的原因。 如何追蹤變化。
但是還是需要一些基礎(chǔ)知識,關(guān)于ES5的Object.defineProperty().
關(guān)于Object.defineProperty
這個函數(shù)接受三個參數(shù),一個參數(shù)是obj,表示要定義屬性的對象,一個參數(shù)是prop,是要定義或者更改的屬性名字,另外是descriptor,描述符,來定義屬性的具體描述。
Object.defineProperty(obj, prop, descriptor)
下面的是實例代碼,obj是一個沒有屬性的空對象,然后"key"是屬性名,{}大括號里面定義了要給屬性賦值的情況,value代表屬性的值,proto代表繼承屬性的性質(zhì),這里面還有其他的選項。比如configurable,enumerable,writable等默認(rèn)是false的。
// using __proto__
var obj = {};
Object.defineProperty(obj, 'key', {
__proto__: null, // no inherited properties
value: 'static' // not enumerable
// not configurable
// not writable
// as defaults
});
我們通過控制臺的結(jié)果來感受一下writable為false的作用。我們發(fā)現(xiàn),就算對"key"屬性重新賦值了,它的屬性仍然保持不變。

descriptors(描述符)分成兩種,一種是data descriptors,另外一種是 accessor descriptors.兩種的descriptors有兩個必選項,configurable和enumerable
configurable
true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object.Defaults to false
.
代表這個屬性的descriptor也就是描述是可以更改的,這個熟悉也能從對象上面刪除,默認(rèn)false,也就是不能更改跟屬性有關(guān)的任意值,如果我重新對這個屬性進行定義的話,會提示出錯,同時也不能刪除。

enumerable
true if and only if this property shows up during enumeration of the properties on the corresponding object.Defaults to false
.
代表這個屬性能夠通過for in或者Object.keys
來遍歷。默認(rèn)為false

A data descriptor有兩個可選項.
value
The value associated with the property. Can be any valid JavaScript value (number, object, function, etc).Defaults to undefined
.
這個選項為屬性賦值,可以是任意的JavaScript值,默認(rèn)為undefined
writable
true if and only if the value associated with the property may be changed with an assignment operator.Defaults to false
.
writable表示能不能夠重寫屬性值,默認(rèn)為false
accessor descriptor也有兩個關(guān)鍵的屬性。
get
A function which serves as a getter for the property, or undefined
if there is no getter. The function return will be used as the value of property.Defaults to undefined
.
set
定義了一個函數(shù),作為屬性的getter,如果沒有g(shù)etter就為undefined 默認(rèn)為undefined
set
A function which serves as a setter for the property, or undefined
if there is no setter. The function will receive as only argument the new value being assigned to the property.Defaults to undefined
.
同get
這里面有一點是,可能會從原型鏈上面繼承相應(yīng)的屬性,如果想避免這種情況,可以寫get。所以可以用__proto__: null
下面是一個可愛的例子
var o = {}; // Creates a new object 創(chuàng)造對象
// Example of an object property added with defineProperty with a data property descriptor
Object.defineProperty(o, 'a', {
value: 37,
writable: true,
enumerable: true,
configurable: true
});
// 'a' property exists in the o object and its value is 37
// Example of an object property added with defineProperty with an accessor property descriptor
var bValue = 38;
Object.defineProperty(o, 'b', {
get: function() { return bValue; },
set: function(newValue) { bValue = newValue; },
enumerable: true,
configurable: true
});
o.b; // 38
// 'b' property exists in the o object and its value is 38
// The value of o.b is now always identical to bValue, unless o.b is redefined
// You cannot try to mix both:
Object.defineProperty(o, 'conflict', {
value: 0x9f91102,
get: function() { return 0xdeadbeef; }
});
// throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors

第一段代表定義了一個data descriptor,第二段代表定義了accessor descriptor,通get定義了取值操作,第三段代碼告訴我們這兩種不能混用。
視圖和數(shù)據(jù)變化綁定
而vue.js主要利用了accessor descriptors的set和get來更新視圖,這里看到的這個例子挺好,是一個簡單的綁定。
對于一個html頁面
<div>
<p>你好,<span id='nickName'></span></p>
<div id="introduce"></div>
</div>
設(shè)置一個數(shù)據(jù)的屬性的getter和setter
//視圖控制器
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;
}
})
然后就能愉快地綁定數(shù)據(jù)交互了。
userInfo.nickName = "xxx";
userInfo.introduce = "我是xxx,我來自云南,..."
vue.js的數(shù)據(jù)變動
但是,這個例子只是數(shù)據(jù)和dom節(jié)點的綁定,而vue.js更為復(fù)雜一點,它在網(wǎng)頁dom和accessor之間會有兩層,一層是Wacher,一層是Directive,比如以下代碼。
var a = { b: 1 }
var vm = new Vue({
data: data
})
把一個普通對象(a={b:1})傳給 Vue 實例作為它的 data 選項,Vue.js 將遍歷它的屬性,用Object.defineProperty 將它們轉(zhuǎn)為 getter/setter,如圖綠色的部分所示。
每次用戶更改data里的數(shù)據(jù)的時候,比如a.b =1,setter就會重新通知Watcher進行變動,Watcher再通知Directive對dom節(jié)點進行更改。
