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

descriptors(描述符)分成兩種,一種是data descriptors,另外一種是 accessor descriptors.兩種的descriptors有兩個(gè)必選項(xiàng),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
.
代表這個(gè)屬性的descriptor也就是描述是可以更改的,這個(gè)熟悉也能從對(duì)象上面刪除,默認(rèn)false,也就是不能更改跟屬性有關(guān)的任意值,如果我重新對(duì)這個(gè)屬性進(jìn)行定義的話,會(huì)提示出錯(cuò),同時(shí)也不能刪除。

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

A data descriptor有兩個(gè)可選項(xiàng).
value
The value associated with the property. Can be any valid JavaScript value (number, object, function, etc).Defaults to undefined
.
這個(gè)選項(xiàng)為屬性賦值,可以是任意的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也有兩個(gè)關(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
定義了一個(gè)函數(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
這里面有一點(diǎn)是,可能會(huì)從原型鏈上面繼承相應(yīng)的屬性,如果想避免這種情況,可以寫get。所以可以用__proto__: null
下面是一個(gè)可愛的例子
var o = {}; // Creates a new object 創(chuàng)造對(duì)象
// 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

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