observable
同時支持 decorator
方式和方法調用方式。數組
// 示例
@observable name = '張三';
const temp = observable.box(20);
複製代碼
在源碼中能夠發現 mobx
爲 observable
上綁定了不少方法。緩存
Object.keys(observableFactories).forEach(function(name) {
return (observable[name] = observableFactories[name]);
});
複製代碼
本篇文章重點介紹
@observable
調用,其他的後續文章會繼續講解。app
@observable
有一個很強大的功能,它會對於不一樣類型,應用不一樣的轉換規則。它到底是如何處理的?函數
當咱們使用 @observable
,實質上是調用了 createObservable
的返回的 deepDecorator
函數。ui
createObservable
接收 3 個參數 v
、arg2
、arg3
,這三個參數分別對應構造函數的原型對象、屬性名、描述符。this
function createObservable(v, arg2, arg3) {
// @observable someProp;
if (typeof arguments[1] === 'string') {
return deepDecorator.apply(null, arguments);
}
...
}
複製代碼
deepDecorator
函數是 createDecoratorForEnhancer
的返回值,它把 deepEnhancer
做爲參數傳遞進去。deepDecorator
的具體功能就是針對不一樣類型使用不一樣方式的 observable
。spa
var deepDecorator = createDecoratorForEnhancer(deepEnhancer);
複製代碼
createDecoratorForEnhancer
函數會返回一個 res
,因此說當調用 @observable
時候就是等於執行 res
函數,傳遞進去的 deepEnhancer
會做爲 res
的 enhancer
屬性存在。code
createDecoratorForEnhancer
方法內部會經過 createPropDecorator
函數生成 res
。createPropDecorator
函數接收 2 個參數,第一個參數 propertyInitiallyEnumerable
設置爲 true
( 內部寫死 ),第二個參數 propertyCreator
爲傳遞進來的函數。對象
createPropDecorator
函數返回 decoratorFactory
函數。看到這裏,咱們先整理下,當咱們編寫 @observable
,實質上就是在調用 decoratorFactory
函數。繼承
decoratorFactory
函數內部定義 decorator
函數,當調用時,會先判斷當前的調用方式,若是是 @decorator
方式調用,則直接執行 decorator
函數,不然返回 decorator
函數。
decorator
函數內部會首先判斷構造函數的原型對象上是否存在 __mobxDecorators
屬性,若是不存在,則定義此屬性,並經過 addHiddenProp
方法設置描述符。
function addHiddenProp(object, propName, value) {
Object.defineProperty(object, propName, {
enumerable: false,
writable: true,
configurable: true,
value: value // 此時爲空對象
});
}
複製代碼
當構造函數原型對象上存在 __mobxDecorators
屬性,則執行下面代碼。
target.__mobxDecorators[prop] = {
prop: prop, // 屬性名
propertyCreator: propertyCreator, // 傳遞進來的函數
descriptor: descriptor, // 描述符
decoratorTarget: target, // 構造函數原型對象
decoratorArguments: decoratorArguments // 此時爲 []
};
複製代碼
最後經過調用 createPropertyInitializerDescriptor
函數爲屬性生成描述符。createPropertyInitializerDescriptor
函數內部會根據是否可枚舉進行分類,並以屬性名做爲緩存對象的 key
,生成的描述符做爲 value
存在。
尤爲須要注意的是,描述符中有 get
和 set
方法,這兩個方法內部都會首先調用 initializeInstance
方法,而後才執行對應的數據操做。
initializeInstance
方法會首先判斷原型對象是否 __mobxDidRunLazyInitializers
屬性,若是存在,則後續都不執行。若是不存在,則會依次調用原型對象上 __mobxDecorators
屬性對應的 propertyCreator
方法。
看到這裏,可能有人就懵了,問 propertyCreator
方法哪來的?還記得咱們在調用 createPropDecorator
函數傳遞進去的第二個參數嗎?這個方法就是那來的。propertyCreator
內部首先會判斷屬性描述符中是否存在 get
,這裏的屬性描述符是原有的屬性描述符,而不是封裝後的。若是存在 get
方法,則報錯,不然繼續執行。判斷描述符是否存在,不存在則設置初始值爲 undefined
,存在則繼續判斷是否有 initializer
方法,若是沒有,則初始值爲描述符的 value
。若是有此方法,不然執行此方法,獲取屬性初始值。
var initialValue = descriptor
? descriptor.initializer
? descriptor.initializer.call(target)
: descriptor.value
: undefined;
複製代碼
初始值獲取以後,調用 defineObservableProperty
方法,傳入 target
構造函數原型對象、propertyName
屬性名、initialValue
初始值和 enhancer
( deepEnhancer
)。
function defineObservableProperty(target, propName, newValue, enhancer) {
var adm = asObservableObject(target);
}
複製代碼
asObservableObject
方法會首先判斷原型對象是否可擴展,若是不能夠,則報錯。其次根據必定規則生成 name
,經過調用 new ObservableObjectAdministration(target, name, defaultEnhancer)
生成 adm
對象,此對象會綁在原型對象的 $mobx
上,並返回新生成的 adm
對象。
defineObservableProperty
首先會經過調用 asObservableObject
方法獲取 adm
對象,判斷原型對象上的屬性是否可配置與可寫,若是不能夠,報錯。判斷新生成的 adm
對象上是否存在 interceptors
屬性,且屬性值得長度大於 0
。
若是不存在,則給 adm
對象的 values
屬性賦值,值爲 ObservableValue
的實例。
ObservableValue
類繼承 Atom
類,在實例化 ObservableValue
同時,會執行 enhancer
方法,在這裏即爲 deepEnhancer
。
var ObservableValue = (function(_super) {
__extends(ObservableValue, _super);
// 部分代碼
var _this = _super.call(this, name) || this;
_this.value = enhancer(value, undefined, name);
})(Atom);
複製代碼
deepEnhancer
會對原型對象進行判斷,若是是 observable
,直接返回原型對象;若是是數組,返回 observable.array
調用後結果;若是是對象,返回 observable.object
調用後結果;若是是 Map
,返回 observable.map
調用後結果;若是是 Set
,返回 observable.set
調用後結果;若是都不是,則直接返回傳進來的 v
。
function deepEnhancer(v, _, name) {
if (isObservable(v)) return v;
if (Array.isArray(v)) return observable.array(v, { name: name });
if (isPlainObject(v)) return observable.object(v, undefined, { name: name });
if (isES6Map(v)) return observable.map(v, { name: name });
if (isES6Set(v)) return observable.set(v, { name: name });
return v;
}
複製代碼
defineObservableProperty
方法最後會覆蓋原型對象原有的屬性描述符,並劫持 get
和 set
操做。
Object.defineProperty(target, propName, generateComputedPropConfig(propName));
function generateComputedPropConfig(){
// 部分
return {
configurable: true,
enumerable: true,
get: function() {
return this.$mobx.read(this, propName);
},
set: function(v) {
this.$mobx.write(this, propName, v);
}
}
}
複製代碼
若是以爲文章不錯,對你有幫助,煩請點贊。