本篇實現的僅僅是對象監聽,不包含數組。關於數組的下一篇會講。vue
Vue給了咱們很好的回答,那就是defineProperty。defineProperty方法有如下幾個參數:react
這個descriptor對象能夠設置的屬性就比較多了:git
其餘的descriptor對象理解應該不難,看了MDN上的教程,應該會發現對於set和get方法的使用,老是會在外部聲明一個變量來維護值的變化,是否是很變扭,而後就會有人嘗試用this來調用屬性,這時就會報執行棧溢出,由於它遞歸的執行本身,致使棧溢出。github
這裏咱們能夠經過set和get內部維護的'_'開頭的私有變量來解決:api
const obj = {};
Object.defineProperty(obj,'name', {
get () {
console.log('讀取name的值爲 => ' + this._name);
return this._name;
},
set (newValue) {
this._name = newValue;
console.log('設置name的值爲 => ' + this._name);
}
})
obj.name = 'xiaoming'; // '設置name的值爲 => xiaoming'
obj.name; // '讀取name的值爲 => xiaoming'複製代碼
兩種方法看着喜愛用吧。數組
首先咱們得有一個對象: dom
const obj = {
a: 1,
b: {
c: 2,
d: 3
}
};複製代碼
接下來咱們要將它變成一個響應式對象,讓它的取值與賦值操做多在咱們的監聽下,首先咱們要經過一個Observer來包裝咱們須要響應化的對象:函數
function Observer(value) {
/** * 構造函數防止沒有使用'new'關鍵字建立 */
if (!(this instanceof Observer)) {
return new Observer(value);
}
this.value = value;
// 這一篇僅僅討論對象的監聽
if (!Array.isArray(value)) {
this.walk(value);
}
}
Observer.prototype.walk = function (obj) {
const keys = Object.keys(obj);
for (let i = 0, max = keys.length; i < max; i++) {
const key = keys[i];
//將對象響應化
defineReactive(obj, key, obj[key]);
}
}複製代碼
接下來核心就是採用defineProperty方法來監聽set和get方法,可是這裏咱們要注意的是採用遞歸建立Observer對象的方式,解決對象嵌套的問題:學習
function defineReactive(obj, key, val) {
//獲取對象的描述器對象
const property = Object.getOwnPropertyDescriptor(obj, key);
//這裏上面強調了configurable屬性的重要性
if (property && property.configurable === false) {
return;
}
const setter = property && property.set;
const getter = property && property.get;
//解決對象嵌套的問題
observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get () {
const value = getter ? getter.call(obj) : val;
log('讀取',obj,key,value);
return value;
},
set (newValue) {
const value = getter ? getter.call(obj) : val;
if (setter) {
setter.call(obj, newValue);
} else {
val = newValue;
}
log('設置',obj,key,value);
}
})
}
function observe(obj) {
if (!isObject(obj)) {
return;
}
return new Observer(obj);
}複製代碼
這裏基本上對象的監聽已經完成了,下面來試試:ui
Observer(obj);
obj.b.d = 10;複製代碼
可是這裏還存在一個問題,由於咱們還有這種操做:
obj.f = 10;複製代碼
這時obj.f再也不被咱們監聽,對於這種狀況在Vue中咱們要經過Vue.set方法設置,這裏咱們也能夠寫了set方法:
function set(target, key, val) {
if(!target.hasOwnProperty(key)) {
target[key] = val;
defineReactive(target, key, val);
}
}複製代碼
這裏是個簡易的set方法,主要思想就是使新屬性響應化。
經過源碼的學習,使你在使用Vue的一些api更加駕輕就熟。
PS:源碼用的flow.js,看起來有點費勁,可是我應該會堅持下去。(下一篇會講數組的監聽)