摘要:支持形式的調用這其實是非常經典的無構造,其實就是一個構造函數,的結果就是一個對象實例,該實例有個屬性,屬性值是。
前言
終于,樓主的「Underscore 源碼解讀系列」underscore-analysis 即將進入尾聲,關注下 timeline 會發現樓主最近加快了解讀速度。十一月,多事之秋,最近好多事情搞的樓主心力憔悴,身心俱疲,也想盡快把這個系列完結掉,也好了卻一件心事。
本文預計是解讀系列的倒數第二篇,最后一篇那么顯然就是大總結了。樓主的 Underscore 系列解讀完整版地址 https://github.com/hanzichi/u...
常規調用之前寫的文章,關注點大多在具體的方法,具體的知識細節,也有讀者留言建議樓主講講整體架構,這是必須會講的,只是樓主把它安排在了最后,也就是本文,因為樓主覺得不掌握整體架構對于具體方法的理解也是沒有大的問題的。
Underscore 大多數時候的調用形式為 _.funcName(xx, xx),這也是 文檔中 的調用方式。
_.each([1, 2, 3], alert);
最簡單的實現方式,我們可以把 _ 看做一個簡單的對象:
var _ = {}; _.each = function() { // ... };
在 JavaScript 中,一切皆對象,實際上,源碼中的 _ 變量是一個方法:
var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; };
為什么會是方法?我們接下去看。
OOPUnderscore 支持 OOP 形式的調用:
_([1, 2, 3]).each(alert);
這其實是非常經典的「無 new 構造」,_ 其實就是一個 構造函數,_([1, 2, 3]) 的結果就是一個對象實例,該實例有個 _wrapped 屬性,屬性值是 [1, 2, 3]。實例要調用 each 方法,其本身沒有這個方法,那么應該來自原型鏈,也就是說 _.prototype 上應該有這個方法,那么,方法是如何掛載上去的呢?
方法掛載現在我們已經明確以下兩點:
_ 是一個函數(支持無 new 調用的構造函數)
_ 的屬性有很多方法,比如 _.each,_.template 等等
我們的目標是讓 _ 的構造實例也能調用這些方法。仔細想想,其實也不難,我們可以遍歷 _ 上的屬性,如果屬性值類型是函數,那么就將函數掛到 _ 的原型鏈上去。
源碼中用來完成這件事的是 _.mixin 方法:
// Add your own custom functions to the Underscore object. // 可向 underscore 函數庫擴展自己的方法 // obj 參數必須是一個對象(JavaScript 中一切皆對象) // 且自己的方法定義在 obj 的屬性上 // 如 obj.myFunc = function() {...} // 形如 {myFunc: function(){}} // 之后便可使用如下: _.myFunc(..) 或者 OOP _(..).myFunc(..) _.mixin = function(obj) { // 遍歷 obj 的 key,將方法掛載到 Underscore 上 // 其實是將方法淺拷貝到 _.prototype 上 _.each(_.functions(obj), function(name) { // 直接把方法掛載到 _[name] 上 // 調用類似 _.myFunc([1, 2, 3], ..) var func = _[name] = obj[name]; // 淺拷貝 // 將 name 方法掛載到 _ 對象的原型鏈上,使之能 OOP 調用 _.prototype[name] = function() { // 第一個參數 var args = [this._wrapped]; // arguments 為 name 方法需要的其他參數 push.apply(args, arguments); // 執行 func 方法 // 支持鏈式操作 return result(this, func.apply(_, args)); }; }); }; // Add all of the Underscore functions to the wrapper object. // 將前面定義的 underscore 方法添加給包裝過的對象 // 即添加到 _.prototype 中 // 使 underscore 支持面向對象形式的調用 _.mixin(_);
_.mixin 方法可以向 Underscore 庫增加自己定義的方法:
_.mixin({ capitalize: function(string) { return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase(); } }); _("fabio").capitalize(); => "Fabio"
同時,Underscore 也加入了一些 Array 原生的方法:
// Add all mutator Array functions to the wrapper. // 將 Array 原型鏈上有的方法都添加到 underscore 中 _.each(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { var obj = this._wrapped; method.apply(obj, arguments); if ((name === "shift" || name === "splice") && obj.length === 0) delete obj[0]; // 支持鏈式操作 return result(this, obj); }; }); // Add all accessor Array functions to the wrapper. // 添加 concat、join、slice 等數組原生方法給 Underscore _.each(["concat", "join", "slice"], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { return result(this, method.apply(this._wrapped, arguments)); }; });鏈式調用
Underscore 也支持鏈式調用:
// 非 OOP 鏈式調用 _.chain([1, 2, 3]) .map(function(a) {return a * 2;}) .reverse() .value(); // [6, 4, 2] // OOP 鏈式調用 _([1, 2, 3]) .chain() .map(function(a){return a * 2;}) .first() .value(); // 2
乍一看似乎有 OOP 和非 OOP 兩種鏈式調用形式,其實只是一種,_.chain([1, 2, 3]) 和 _([1, 2, 3]).chain() 的結果是一樣的。如何實現的?我們深入 chain 方法看下。
_.chain = function(obj) { // 無論是否 OOP 調用,都會轉為 OOP 形式 // 并且給新的構造對象添加了一個 _chain 屬性 var instance = _(obj); // 標記是否使用鏈式操作 instance._chain = true; // 返回 OOP 對象 // 可以看到該 instance 對象除了多了個 _chain 屬性 // 其他的和直接 _(obj) 的結果一樣 return instance; };
我們看下 _.chain([1, 2, 3]) 的結果,將參數代入函數中,其實就是對參數進行無 new 構造,然后返回實例,只是實例多了個 _chain 屬性,其他的和直接 _([1, 2, 3]) 一模一樣。再來看 _([1, 2, 3]).chain(),_([1, 2, 3]) 返回構造實例,該實例有 chain 方法,調用方法,為實例添加 _chain 屬性,返回該實例對象。所以,這兩者效果是一致的,結果都是轉為了 OOP 的形式。
說了這么多,似乎還沒講到正題上,它是如何「鏈」下去的?我們以如下代碼為例:
_([1, 2, 3]) .chain() .map(function(a){return a * 2;}) .first() .value(); // 2
當調用 map 方法的時候,實際上可能會有返回值。我們看下 _.mixin 源碼:
// 執行 func 方法 // 支持鏈式操作 return result(this, func.apply(_, args));
result 是一個重要的內部幫助函數(Helper function ):
// Helper function to continue chaining intermediate results. // 一個幫助方法(Helper function) var result = function(instance, obj) { // 如果需要鏈式操作,則對 obj 運行 chain 方法,使得可以繼續后續的鏈式操作 // 如果不需要,直接返回 obj return instance._chain ? _(obj).chain() : obj; };
如果需要鏈式操作(實例會有帶有 _chain 屬性),則對運算結果調用 chain 函數,使之可以繼續鏈式調用。
小結Underscore 整體架構,或者說是基礎實現大概就是這個樣子,代碼部分就講到這了,接下去系列解讀最后一篇,講講這段時間(幾乎也是歷時半年了)的一些心得體會吧,沒錢的就捧個人場吧!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/80737.html
摘要:譯立即執行函數表達式處理支持瀏覽器環境微信小程序。學習整體架構,利于打造屬于自己的函數式編程類庫。下一篇文章可能是學習的源碼整體架構。也可以加微信,注明來源,拉您進前端視野交流群。 前言 上一篇文章寫了jQuery整體架構,學習 jQuery 源碼整體架構,打造屬于自己的 js 類庫 雖然看過挺多underscore.js分析類的文章,但總感覺少點什么。這也許就是紙上得來終覺淺,絕知此...
摘要:我這里有個不夠準確但容易理解的說法,就是檢查一個對象是否為另一個構造函數的實例,為了更容易理解,下面將全部以是的實例的方式來說。 underscore源碼分析之整體架構 最近打算好好看看underscore源碼,一個是因為自己確實水平不夠,另一個是underscore源碼比較簡單,比較易讀。本系列打算對underscore1.8.3中關鍵函數源碼進行分析,希望做到最詳細的源碼分析。今...
摘要:數據庫系統一旦出現問題無法提供服務,有可能導致整個系統都無法繼續工作。所以,一個成功的數據庫架構在高可用設計方面也是需要充分考慮的。操作保持順序,可滿足數據庫對數據一致性的苛刻要求。 數據庫是所有應用系統的核心,故保證數據庫穩定、高效、安全地運行是所有企業日常工作的重中之重。數據庫系統一旦出現問題無法提供服務,有可能導致整個系統都無法繼續工作。所以,一個成功的數據庫架構在高可用設計方面...
閱讀 1985·2021-09-26 10:19
閱讀 3257·2021-09-24 10:25
閱讀 1641·2019-12-27 11:39
閱讀 1929·2019-08-30 15:43
閱讀 670·2019-08-29 16:08
閱讀 3509·2019-08-29 16:07
閱讀 909·2019-08-26 11:30
閱讀 1273·2019-08-26 10:41