搜尋
首頁web前端js教程Underscore整體架構淺析

前言

終於,樓主的「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; 
};

為什麼會是方法?我們接下去去看。

OOP

Underscore 支援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 也加入了一些Arraycore 原生的方法:

// 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) {

_.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 函數,使之可以繼續鍊式呼叫。



陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
在JavaScript中替換字符串字符在JavaScript中替換字符串字符Mar 11, 2025 am 12:07 AM

JavaScript字符串替換方法詳解及常見問題解答 本文將探討兩種在JavaScript中替換字符串字符的方法:在JavaScript代碼內部替換和在網頁HTML內部替換。 在JavaScript代碼內部替換字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 該方法僅替換第一個匹配項。要替換所有匹配項,需使用正則表達式並添加全局標誌g: str = str.replace(/fi

jQuery檢查日期是否有效jQuery檢查日期是否有效Mar 01, 2025 am 08:51 AM

簡單JavaScript函數用於檢查日期是否有效。 function isValidDate(s) { var bits = s.split('/'); var d = new Date(bits[2] '/' bits[1] '/' bits[0]); return !!(d && (d.getMonth() 1) == bits[1] && d.getDate() == Number(bits[0])); } //測試 var

jQuery獲取元素填充/保證金jQuery獲取元素填充/保證金Mar 01, 2025 am 08:53 AM

本文探討如何使用 jQuery 獲取和設置 DOM 元素的內邊距和外邊距值,特別是元素外邊距和內邊距的具體位置。雖然可以使用 CSS 設置元素的內邊距和外邊距,但獲取準確的值可能會比較棘手。 // 設定 $("div.header").css("margin","10px"); $("div.header").css("padding","10px"); 你可能會認為這段代碼很

10個jQuery手風琴選項卡10個jQuery手風琴選項卡Mar 01, 2025 am 01:34 AM

本文探討了十個特殊的jQuery選項卡和手風琴。 選項卡和手風琴之間的關鍵區別在於其內容面板的顯示和隱藏方式。讓我們深入研究這十個示例。 相關文章:10個jQuery選項卡插件

10值得檢查jQuery插件10值得檢查jQuery插件Mar 01, 2025 am 01:29 AM

發現十個傑出的jQuery插件,以提升您的網站的活力和視覺吸引力!這個精選的收藏品提供了不同的功能,從圖像動畫到交互式畫廊。讓我們探索這些強大的工具:相關文章:1

HTTP與節點和HTTP-Console調試HTTP與節點和HTTP-Console調試Mar 01, 2025 am 01:37 AM

HTTP-Console是一個節點模塊,可為您提供用於執行HTTP命令的命令行接口。不管您是否針對Web服務器,Web Serv

自定義Google搜索API設置教程自定義Google搜索API設置教程Mar 04, 2025 am 01:06 AM

本教程向您展示瞭如何將自定義的Google搜索API集成到您的博客或網站中,提供了比標準WordPress主題搜索功能更精緻的搜索體驗。 令人驚訝的是簡單!您將能夠將搜索限制為Y

jQuery添加捲軸到DivjQuery添加捲軸到DivMar 01, 2025 am 01:30 AM

當div內容超出容器元素區域時,以下jQuery代碼片段可用於添加滾動條。 (無演示,請直接複製到Firebug中) //D = document //W = window //$ = jQuery var contentArea = $(this), wintop = contentArea.scrollTop(), docheight = $(D).height(), winheight = $(W).height(), divheight = $('#c

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
2 週前By尊渡假赌尊渡假赌尊渡假赌
倉庫:如何復興隊友
4 週前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器