observable
同時支持 decorator
方式和方法調用方式。
// 示例
@observable name = '張三';
const temp = observable.box(20);
在源碼中可以發現 mobx
爲 observable
上綁定了很多方法。
Object.keys(observableFactories).forEach(function(name) {
return (observable[name] = observableFactories[name]);
});
本篇文章重點介紹 @observable
調用,其餘的後續文章會繼續講解。
@observable
有一個很強大的功能,它會對於不同類型,應用不同的轉換規則。它究竟是如何處理的?
createObservable
當我們使用 @observable
,實質上是調用了 createObservable
的返回的 deepDecorator
函數。
createObservable
接收 3 個參數 v
、arg2
、arg3
,這三個參數分別對應構造函數的原型對象、屬性名、描述符。
function createObservable(v, arg2, arg3) {
// @observable someProp;
if (typeof arguments[1] === 'string') {
return deepDecorator.apply(null, arguments);
}
...
}
createDecoratorForEnhancer
deepDecorator
函數是 createDecoratorForEnhancer
的返回值,它把 deepEnhancer
作爲參數傳遞進去。deepDecorator
的具體功能就是針對不同類型使用不同方式的 observable
。
var deepDecorator = createDecoratorForEnhancer(deepEnhancer);
createDecoratorForEnhancer
函數會返回一個 res
,所以說當調用 @observable
時候就是等於執行 res
函數,傳遞進去的 deepEnhancer
會作爲 res
的 enhancer
屬性存在。
createPropDecorator
createDecoratorForEnhancer
方法內部會通過 createPropDecorator
函數生成 res
。createPropDecorator
函數接收 2 個參數,第一個參數 propertyInitiallyEnumerable
設置爲 true
( 內部寫死 ),第二個參數 propertyCreator
爲傳遞進來的函數。
createPropDecorator
函數返回 decoratorFactory
函數。看到這裏,我們先整理下,當我們編寫 @observable
,實質上就是在調用 decoratorFactory
函數。
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
函數爲屬性生成描述符。createPropertyInitializerDescriptor
函數內部會根據是否可枚舉進行分類,並以屬性名作爲緩存對象的 key
,生成的描述符作爲 value
存在。
尤其需要注意的是,描述符中有 get
和 set
方法,這兩個方法內部都會首先調用 initializeInstance
方法,然後才執行對應的數據操作。
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
初始值獲取之後,調用 defineObservableProperty
方法,傳入 target
構造函數原型對象、propertyName
屬性名、initialValue
初始值和 enhancer
( deepEnhancer
)。
function defineObservableProperty(target, propName, newValue, enhancer) {
var adm = asObservableObject(target);
}
asObservableObject
asObservableObject
方法會首先判斷原型對象是否可擴展,如果不可以,則報錯。其次根據一定規則生成 name
,通過調用 new ObservableObjectAdministration(target, name, defaultEnhancer)
生成 adm
對象,此對象會綁在原型對象的 $mobx
上,並返回新生成的 adm
對象。
defineObservableProperty
首先會通過調用 asObservableObject
方法獲取 adm
對象,判斷原型對象上的屬性是否可配置與可寫,如果不可以,報錯。判斷新生成的 adm
對象上是否存在 interceptors
屬性,且屬性值得長度大於 0
。
如果不存在,則給 adm
對象的 values
屬性賦值,值爲 ObservableValue
的實例。
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);
}
}
}
如果覺得文章不錯,對你有幫助,煩請點贊。