摘要:本文同步自我得博客最近準備折騰一下,在事先了解了之后,我知道了對這個庫有著強依賴,正好之前也沒使用過,于是我就想先把徹底了解一下,這樣之后折騰的時候也少一點阻礙。
本文同步自我得博客:http://www.joeray61.com
最近準備折騰一下backbone.js,在事先了解了backbone之后,我知道了backbone對underscore這個庫有著強依賴,正好underscore之前也沒使用過,于是我就想先把underscore徹底了解一下,這樣之后折騰backbone的時候也少一點阻礙。
*underscore*是一個很實用且小巧的框架,提供了很多我們在編程時需要的基本功能函數(shù),而且他沒有擴展*javascript*的原生對象,主要涉及對*Object*、*Array*、*Function*的操作。 我曾經問我的朋友[@小胡子哥][1] 怎么學習一個框架?他給我的回答是:“直接看源碼?!爆F(xiàn)在想想深感同意,因為研究源碼是最直接的學習途徑,可以深入地了解這個框架的思想和精髓,同時也能學習框架作者的編程技巧,提升自己的coding水平。 好了,題外話就說到這里,下面咱們進入正題。簡化源碼看結構
我這次看的underscore版本是1.3.3,整個文件也就1000多行,我把代碼簡化了一下,并加入了相關的注釋:
// underscore的代碼包裹在一個匿名自執(zhí)行函數(shù)中 (function() { // 創(chuàng)建一個全局對象, 在瀏覽器中表示為window對象, 在Node.js中表示global對象 var root = this; // 保存"_"(下劃線變量)被覆蓋之前的值 // 如果出現(xiàn)命名沖突或考慮到規(guī)范, 可通過_.noConflict()方法恢復"_"被Underscore占用之前的值, 并返回Underscore對象以便重新命名 var previousUnderscore = root._; // 創(chuàng)建一個空的對象常量, 便于內部共享使用 var breaker = {}; // 將內置對象的原型鏈緩存在局部變量 var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; // 將內置對象原型中的常用方法緩存在局部變量 var slice = ArrayProto.slice, unshift = ArrayProto.unshift, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; // 這里定義了一些JavaScript 1.6提供的新方法 // 如果宿主環(huán)境中支持這些方法則優(yōu)先調用, 如果宿主環(huán)境中沒有提供, 則會由Underscore實現(xiàn) var nativeForEach = ArrayProto.forEach, nativeMap = ArrayProto.map, nativeReduce = ArrayProto.reduce, nativeReduceRight = ArrayProto.reduceRight, nativeFilter = ArrayProto.filter, nativeEvery = ArrayProto.every, nativeSome = ArrayProto.some, nativeIndexOf = ArrayProto.indexOf, nativeLastIndexOf = ArrayProto.lastIndexOf, nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeBind = FuncProto.bind; // 創(chuàng)建對象式的調用方式, 將返回一個Underscore包裝器, 包裝器對象的原型中包含Underscore所有方法(類似與將DOM對象包裝為一個jQuery對象) var _ = function(obj) { // 所有Underscore對象在內部均通過wrapper對象進行構造 return new wrapper(obj); }; // 針對不同的宿主環(huán)境, 將Undersocre的命名變量存放到不同的對象中 if( typeof exports !== "undefined") {// Node.js環(huán)境 if( typeof module !== "undefined" && module.exports) { exports = module.exports = _; } exports._ = _; } else {// 瀏覽器環(huán)境中Underscore的命名變量被掛在window對象中 root["_"] = _; } // 版本聲明 _.VERSION = "1.3.3"; //在_對象上定義各種方法 . . . . . . // underscore對象的包裝函數(shù) var wrapper = function(obj) { // 原始數(shù)據(jù)存放在包裝對象的_wrapped屬性中 this._wrapped = obj; }; // 將Underscore的原型對象指向wrapper的原型, 因此通過像wrapper原型中添加方法, Underscore對象也會具備同樣的方法 _.prototype = wrapper.prototype; // 返回一個對象, 如果當前Underscore調用了chain()方法(即_chain屬性為true), 則返回一個被包裝的Underscore對象, 否則返回對象本身 // result函數(shù)用于在構造方法鏈時返回Underscore的包裝對象 var result = function(obj, chain) { return chain ? _(obj).chain() : obj; }; // 將一個自定義方法添加到Underscore對象中(實際是添加到wrapper的原型中, 而Underscore對象的原型指向了wrapper的原型) var addToWrapper = function(name, func) { // 向wrapper原型中添加一個name函數(shù), 該函數(shù)調用func函數(shù), 并支持了方法鏈的處理 wrapper.prototype[name] = function() { // 獲取func函數(shù)的參數(shù), 并將當前的原始數(shù)據(jù)添加到第一個參數(shù) var args = slice.call(arguments); unshift.call(args, this._wrapped); // 執(zhí)行函數(shù)并返回結果, 并通過result函數(shù)對方法鏈進行封裝, 如果當前調用了chain()方法, 則返回封裝后的Underscore對象, 否則返回對象本身 return result(func.apply(_, args), this._chain); }; }; // 將內部定義的_(即Underscore方法集合對象)中的方法復制到wrapper的原型鏈中(即Underscore的原型鏈中) // 這是為了在構造對象式調用的Underscore對象時, 這些對象也會具有內部定義的Underscore方法 _.mixin(_); // 將Array.prototype中的相關方法添加到Underscore對象中, 因此在封裝后的Underscore對象中也可以直接調用Array.prototype中的方法 // 如: _([]).push() each(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function(name) { // 獲取Array.prototype中對應方法的引用 var method = ArrayProto[name]; // 將該方法添加到Underscore對象中(實際是添加到wrapper的原型對象, 因此在創(chuàng)建Underscore對象時同時具備了該方法) wrapper.prototype[name] = function() { // _wrapped變量中存儲Underscore對象的原始值 var wrapped = this._wrapped; // 調用Array對應的方法并返回結果 method.apply(wrapped, arguments); var length = wrapped.length; if((name == "shift" || name == "splice") && length === 0) delete wrapped[0]; // 即使是對于Array中的方法, Underscore同樣支持方法鏈操作 return result(wrapped, this._chain); }; }); // 作用同于上一段代碼, 將數(shù)組中的一些方法添加到Underscore對象, 并支持了方法鏈操作 // 區(qū)別在于上一段代碼所添加的函數(shù), 均返回Array對象本身(也可能是封裝后的Array), concat, join, slice方法將返回一個新的Array對象(也可能是封裝后的Array) each(["concat", "join", "slice"], function(name) { var method = ArrayProto[name]; wrapper.prototype[name] = function() { return result(method.apply(this._wrapped, arguments), this._chain); }; }); // 對Underscore對象進行鏈式操作的聲明方法 wrapper.prototype.chain = function() { // this._chain用來標示當前對象是否使用鏈式操作 // 對于支持方法鏈操作的數(shù)據(jù), 一般在具體方法中會返回一個Underscore對象, 并將原始值存放在_wrapped屬性中, 也可以通過value()方法獲取原始值 this._chain = true; return this; }; // 返回被封裝的Underscore對象的原始值(存放在_wrapped屬性中) wrapper.prototype.value = function() { return this._wrapped; }; }).call(this);小結
underscore這個庫的結構(或者說思路)大致是這樣的:
創(chuàng)建一個包裝器, 將一些原始數(shù)據(jù)進行包裝,所有的*undersocre*對象, 內部均通過*wrapper*函數(shù)進行構造和封裝 *underscore*與*wrapper*的內部關系是:
內部定義變量_, 將underscore相關的方法添加到_, 這樣就可以支持函數(shù)式的調用, 如_.bind()
內部定義wrapper類, 將_的原型對象指向wrapper類的原型
將underscore相關的方法添加到wrapper原型, 創(chuàng)建的_對象就具備了underscore的方法
將Array.prototype相關方法添加到wrapper原型, 創(chuàng)建的_對象就具備了Array.prototype中的方法
new _()時實際創(chuàng)建并返回了一個wrapper()對象, 并將原始數(shù)組存儲到_wrapped變量, 并將原始值作為第一個參數(shù)調用對應方法
之后我會對underscore中所有方法的具體實現(xiàn)進行介紹,感謝關注
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.hztianpu.com/yun/78128.html
摘要:本文同步自我得博客我在這個系列的第一篇文章說過,我學是為了在學的時候少一些阻礙,從第一篇的寫作時間到今天,大概也有個十幾二十天,感覺拖得有點久,所以今天將會是源碼解析系列的最后一篇文章,我會在這篇文章中介紹剩下的所有函數(shù)。 本文同步自我得博客:http://www.joeray61.com 我在這個系列的第一篇文章說過,我學underscore是為了在學backbone的時候少一些阻礙...
摘要:本文同步自我得博客最近十幾天都在忙畢業(yè)論文的事,所以上一次為大家介紹完這個框架的結構或者說是這個框架的設計思路之后就一直沒動靜了,今天我又滿血復活了,讓我們繼續(xù)來探索的源碼奧秘吧。 本文同步自我得博客:http://www.joeray61.com 最近十幾天都在忙畢業(yè)論文的事,所以上一次為大家介紹完underscore這個框架的結構(或者說是這個框架的設計思路)之后就一直沒動靜了,今...
摘要:總想找個機會夯實一下自己的基礎,正好最近略有清閑,看視頻讀書擼代碼我選擇了第三者怎么感覺有點別扭,看視頻的話效率不高適合入門,看書的話一本你不知道的推薦給大家,選擇繼續(xù)看書的話還是算了吧,畢竟讀萬卷書不如行萬里路是吧。 總想找個機會夯實一下自己的JS基礎,正好最近略有清閑,看視頻?讀書?擼代碼?我選擇了第三者(怎么感覺有點別扭),看視頻的話效率不高適合入門,看書的話,一本《你不知道的J...
摘要:創(chuàng)建一個全局對象在瀏覽器中表示為對象在中表示對象保存下劃線變量被覆蓋之前的值如果出現(xiàn)命名沖突或考慮到規(guī)范可通過方法恢復被占用之前的值并返回對象以便重新命名創(chuàng)建一個空的對象常量便于內部共享使用將內置對象的原型鏈緩存在局部變量方便快速調用將 // Underscore.js 1.3.3 // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc....
說明1、源碼結構通覽,簡單注釋說明2、通過調用方法講解核心代碼邏輯 一、源碼的結構 為了方便比對源碼,按源碼的結構順序展示。underscore是個輕量級的工具庫,大部分代碼是實現(xiàn)特定功能以函數(shù)的形式存在,本身會比較簡單,沒對方法具體說明,可直接參考underscore中文文檔 (function() { var root = this; var previousUnderscore = ...
閱讀 3527·2021-11-17 17:00
閱讀 3933·2021-08-09 13:46
閱讀 2937·2019-08-30 15:54
閱讀 711·2019-08-30 13:54
閱讀 3014·2019-08-29 17:13
閱讀 3319·2019-08-29 14:00
閱讀 3049·2019-08-29 11:11
閱讀 1479·2019-08-26 10:15