1 、 defineProperty 重定義對象javascript
JS原生es5版本提供對象從新定義的接口 defineProperty java
defineProperty 能夠修改對象的訪問器屬性,對象屬性值發生變化先後能夠觸發回調函數。react
對象的訪問器屬性包括 2 種類型:數據描述符、 存取描述符數組
1.1 數據描述符
value:對象key的值,默認是 空字符串 ''
writeable:是否可寫,默認 true
configurable:true是否可配置,默認 true
enumerable:true是否可枚舉, 默認 true異步
Object.getOwnPropertyDescriptors(obj); { k:{ configurable: true, enumerable: true, value: 90, writable: true } }
1.2 存取描述符async
set:function(){}屬性訪問器 進行寫操做時調用該方法
get:function(){}屬性訪問器 進行讀操做時調用該方法
屬性描述符: configurable 、enumerable
configurable 、enumerable、 set 、 get函數
對象中新增key的value發生變化時會通過set和get方法。ui
var obj = {};
var temp = ''; Object.defineProperty(obj, 'name', { configurable: true, enumerable: true, get: function () { return temp; }, set: function (newValue) { temp = newValue } }); // 須要維護一個可訪問的變量 temp
或寫在 obj對象內,以下:this
var obj = { tempValue: 'duyi', get name () { return this.tempValue; }, set name (newValue) { this.tempValue = newValue; } }; obj.name = 10; console.log( obj.name ); // 10
小結一次:到這裏來基本上知道 getter setter是能夠實現的,基於這個簡單理論做出一個複雜邏輯。es5
2 、Observer 源碼
/** * Observer類方法將對象修改成可被觀察。 * 一旦應用了這個類方法, 對象的每個key會被轉換成 getter/setter * 用於收集依賴項和觸發更新。 */ var Observer = function Observer (value) { this.value = value; // 保存被觀察的值到方法的實例屬性 this.dep = new Dep(); // 創建一個Dep實例 this.vmCount = 0; // vmCount 記錄vm結構的個數 def(value, '__ob__', this); // value 對象 增長 ‘__ob__’ key,並賦值 this if (Array.isArray(value)) { // value 對象若是是數組 if (hasProto) { // 表示在[]上可使用 __proto__
protoAugment(value, arrayMethods); // 將arrayMethods 添加到value的__proto__。arrayMethods 好像是重寫了數組的一部分原生方法,後面再看 } else { copyAugment(value, arrayMethods, arrayKeys); // 說不定他不支持 __proto__ ,調用def方法加強對象。 } this.observeArray(value); // 而後將這個加強後的數組,每一項都執行observe } else { this.walk(value); // walk 在Observer的原型上,對象轉換爲 getter setter。 } }; /** * 遍歷全部屬性將它們轉換爲 getter/setter,僅當值類型爲對象時調用。 */ Observer.prototype.walk = function walk (obj) { var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { defineReactive$$1(obj, keys[i]); // defineReactive$$1 () 方法,這個方法纔是實現 getter / setter 的原方法!!! } }; /** * 觀察數組項列表 */ Observer.prototype.observeArray = function observeArray (items) { for (var i = 0, l = items.length; i < l; i++) { observe(items[i]); // observe 方法 } }; /** * 截獲原型鏈使用 __proto__ 的方式來 * 加強一個目標的對象或數組,簡稱原型加強。 */ function protoAugment (target, src) { /* eslint-disable no-proto */ target.__proto__ = src; /* eslint-enable no-proto */ } /** * 增長對象原型properties */ function copyAugment (target, src, keys) { for (var i = 0, l = keys.length; i < l; i++) { var key = keys[i]; def(target, key, src[key]); } } /**
* 在Observer方法的原型屬性 observerArray 上有用到!
* 大概意思是建立可觀察實例,返回可觀察實例或已有的可觀察對象。 */ function observe (value, asRootData) {
// 若是不是object時,或是 VNode 的實例不進行 observe if (!isObject(value) || value instanceof VNode) { return } var ob; if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__; } else if ( shouldObserve && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue ) { ob = new Observer(value); } if (asRootData && ob) { ob.vmCount++; } return ob } /** * 這裏是實現getter setter的關鍵代碼
* 這裏是實現getter setter的關鍵代碼
* 這裏是實現getter setter的關鍵代碼
*
* 在對象上定義一個 有反應的原型
* 傳參:obj對象,key關鍵字,val值,customSetter在set的時候會被執行(第4個參數),shallow 默認爲undefined,爲 true 時不執行 observe 函數。
*/
function defineReactive$$1 ( obj, key, val, customSetter, shallow ) { var dep = new Dep(); var property = Object.getOwnPropertyDescriptor(obj, key); if (property && property.configurable === false) { return } // 預約義的getter和setter var getter = property && property.get; var setter = property && property.set; if ((!getter || setter) && arguments.length === 2) { val = obj[key]; } var childOb = !shallow && observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { var value = getter ? getter.call(obj) : val; if (Dep.target) { dep.depend(); if (childOb) { childOb.dep.depend(); if (Array.isArray(value)) { dependArray(value); } } } return value }, set: function reactiveSetter (newVal) { var value = getter ? getter.call(obj) : val; /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return } /* eslint-enable no-self-compare */ if (customSetter) { customSetter(); } // #7981: for accessor properties without setter if (getter && !setter) { return } if (setter) { setter.call(obj, newVal); } else { val = newVal; } childOb = !shallow && observe(newVal); dep.notify(); } }); }
訂閱observe對象的變化,通知觸發update,參考訂閱-發佈模式。
/** * Dep 是能夠有多個指令訂閱的可觀察對象,目的就是對一個目標深層處理 */ var uid = 0; var Dep = function Dep () { this.id = uid++; // 添加了2個實例屬性,id 用於排序和 subs 數組統計sub this.subs = []; }; // 在 subs中添加 sub Dep.prototype.addSub = function addSub (sub) { this.subs.push(sub); }; // 從 subs中移除 sub Dep.prototype.removeSub = function removeSub (sub) { remove(this.subs, sub); }; Dep.prototype.depend = function depend () { if (Dep.target) { Dep.target.addDep(this); // addDep 是 Watcher 的原型方法,用於指令增長依賴 } }; Dep.prototype.notify = function notify () { // 穩定訂閱列表 var subs = this.subs.slice(); if (!config.async) { // 若是不是在異步運行,在程序調度中 subs 不能夠被排序! // 而後排序以確保正確的順序。 subs.sort(function (a, b) { return a.id - b.id; }); } for (var i = 0, l = subs.length; i < l; i++) { // 而後順序觸發 Watcher 原型的 update 方法 subs[i].update(); } }; // 當前目標程序被評估,這個評估全局惟一,一次只要一個觀察者能夠被評估 Dep.target = null; var targetStack = []; // 將目標程序推送到目標棧 function pushTarget (target) { targetStack.push(target); Dep.target = target; } // 執行出棧先去掉 function popTarget () { targetStack.pop(); Dep.target = targetStack[targetStack.length - 1]; }
全局def方法,定義對象。
// def 方法比較簡單 /** * 定義一個原型 * obj * key 對象的key關鍵字 * val 對象的value值 * 是否可枚舉,默承認寫可配置 */ function def (obj, key, val, enumerable) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true }); }