很早就想研究underscore源碼了,雖然underscore.js這個庫有些過期了,可是我仍是想學習一下庫的架構,函數式編程以及經常使用方法的編寫這些方面的內容,又剛好沒什麼其它要研究的了,因此就告終研究underscore源碼這一心願吧。html
underscore.js源碼研究(1)
underscore.js源碼研究(2)
underscore.js源碼研究(3)
underscore.js源碼研究(4)
underscore.js源碼研究(5)
underscore.js源碼研究(6)
underscore.js源碼研究(7)
underscore.js源碼研究(8)git
參考資料:underscore.js官方註釋,undersercore 源碼分析,undersercore 源碼分析 segmentfault編程
對於一個對象的方法,咱們能夠在方法裏面return this來使它支持鏈式調用。若是不修改原方法的源碼,咱們能夠經過下面的函數使它支持鏈式調用。segmentfault
//其中obj是一個對象,functions是對象的方法名數組 function chained(obj, functions) { //因爲functions是一個數組,因此可使用foreach functions.forEach((funcName) => { const func = obj[funcName]; //修改原方法 obj[funcName] = function() { func.apply(this, arguments); return this; } }) }
示例以下:api
let speaker = { haha() { console.log('haha'); }, yaya() { console.log('yaya'); }, lala() { console.log('lala'); } } chained(speaker, ['haha', 'lala', 'yaya']); speaker.haha().yaya().haha().lala().yaya();
輸出以下:數組
haha yaya haha lala yaya
underscore.js裏面的鏈式調用與上面的稍微有些不一樣。架構
它的機制是這樣的:app
下面來具體看看是怎麼運做的:函數式編程
首先,利用下面的函數,能夠獲得一個數組,這個數組裏面全是掛載到_下面的方法名字。函數
_function = _.methods = function(obj) { var names = []; for( var key in obj) { if(_.isFunction(obj[key])) names.push(key); } return names.sort(); };
而後咱們對每個方法名字,把它掛載到_.prototype下面:
_.mixin = function(obj) { _.each(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { var args = [this._wrapped]; push.apply(args, arguments); return chainResult(this, func.apply(_, args)); }; }); }; _.mixin(_);
從上面能夠看到,在掛載的時候返回一個chainResult方法處理過的東西。而args就等於原對象+參數1+參數2+...組成的數組,因此func.apply(_, args)
就是_[func](原對象,參數1,參數2,...)
處理後的結果。
再來看看chainResult是怎麼樣的:
var chainResult = function(instance, obj){ return instance._chain ? _(obj).chain() : obj; };
它判斷若是原對象可以鏈式調用,那麼處理後的結果obj也可以鏈式調用。怎麼讓結果也能鏈式調用呢?答案是使用_.chain方法:
_chain = function(obj) { var instance = _(obj); instance._chain = true; return instance; }
這個方法和咱們最開始的chained方法差很少,可是它會把原對象轉化爲underscore對象,而且這個對象的**_chain屬性爲真**,即它可以被鏈式調用。
因此若是咱們要在underscore.js中實現鏈式調用的話,直接用chain方法便可,實例以下:
_([1,2]).push(3).push(5) //輸出4,並非咱們想要的 _([1,2]).chain().push(3).push(5).value() //輸出[1, 2, 3, 5],正是咱們想要的
能夠看到,咱們上面用到了value()函數,它可以中斷鏈式調用,並不返回指針而是返回值,代碼以下:
_.prototype.value = function() { return this._wrapped; }
等等,_wrapped又是什麼?它是生成underscore對象前的原對象,看下面的代碼:
var _ = function(obj) { if(obj instanceof _) return obj; if(!(this instanceof _)) return new _(obj); this._wrapped = obj; }
也就是說,在使用_生成underscore對象的時候,把原對象儲存在**_wrapped屬性**裏面了,因此_wrapped就是原對象。