成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Underscore 源碼(一)總體架構(gòu)

zhunjiee / 3196人閱讀

摘要:不過這樣子又回帶來另一個(gè)問題,對(duì)于函數(shù),函數(shù)返回什么不重要,主要是處理過程,可以支持鏈?zhǔn)秸{(diào)用,對(duì)于函數(shù),返回的是處理后的結(jié)果,可以不用鏈?zhǔn)?,所以函?shù)就是來判斷是否需要鏈?zhǔn)?,而?duì)返回值進(jìn)行處理。然后后面還有一個(gè)函數(shù),也是用來作為回調(diào)函數(shù)的。

其實(shí),學(xué)習(xí)一個(gè)庫的源碼,最重要的就是先理清它的基本架構(gòu),jQuery 是這樣,Underscore 也應(yīng)該是這樣。

Underscore 這個(gè)庫提供力很多有用的函數(shù),這些函數(shù)部分已經(jīng)在 es5 或 es6 中支持了,比如我們常用的 map、reduce、each,還有 es6 中的 keys 方法等,因?yàn)檫@些方法比較好用,所以被 javascript 的制定者采納了。

先過一遍源碼

我看的版本是 1.8.3,網(wǎng)上很多舊版本的,貌似有很多函數(shù)都已經(jīng)啟用或改變了,有點(diǎn)不一樣啦。

打開源碼,會(huì)看到函數(shù)的基本架構(gòu):

(function(){
  ...
}.call(this))

這和我們常見的閉包不太一樣啊,但是功能都是類似的,在函數(shù)內(nèi)執(zhí)行,防止對(duì)全局變量進(jìn)行污染,然后在函數(shù)的最后調(diào)用 call 函數(shù),把 函數(shù)內(nèi)部的 this 和全局的 this 進(jìn)行綁定。如果在瀏覽器里執(zhí)行,this 會(huì)指向 window,在 node 環(huán)境下,會(huì)指向全局的 global。當(dāng)厭倦使用閉包的時(shí)候,這種方法也是一種不錯(cuò)的體驗(yàn)。

那么接著向下看:

(function(){
  var root = this; // 用 root 來保存當(dāng)前的 this
  var previousUnderscore = root._; // 萬一 _ 之前被占用了,先備份

  // 下面是一些原型,包括 數(shù)組,對(duì)象和函數(shù)
  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;

  var
    push             = ArrayProto.push,
    slice            = ArrayProto.slice,
    toString         = ObjProto.toString,
    hasOwnProperty   = ObjProto.hasOwnProperty;

  var
    nativeIsArray      = Array.isArray,
    nativeKeys         = Object.keys,
    nativeBind         = FuncProto.bind,
    nativeCreate       = Object.create;
}.call(this))

在源碼中搜索 previousUnderscore,可以找到兩處,另外一處就是:

_.noConflict = function() {
  root._ = previousUnderscore;
  return this;
};

noConflict 函數(shù)的用法是可以讓用戶自定義變量來替代 _,并且把之前保存的 _ 給還原,比如:

// 測(cè)試使用
var _ = "Hello World"; // _ 已經(jīng)被占用
...

var us = _.noConflict();
us // 指向 underscore
_ // ‘Hello World"

像 push slice 這些函數(shù),原生都已經(jīng)支持了,源碼里面直接把他們拿過來使用。

來看看 _ 是如何定義的

_ 在 underscore 地位是非常核心的,而它的本質(zhì)實(shí)際上還是函數(shù),同樣也是一個(gè)對(duì)象:

var _ = function(obj) {
  if (obj instanceof _) return obj;
  if (!(this instanceof _)) return new _(obj);
  this._wrapped = obj;
};
_.VERSION = "1.8.3";
_.map = _.collect = function(){...};
_.each = _.forEach = function(){...};

_ 是一個(gè)函數(shù),但是在源碼中,它是被當(dāng)作對(duì)象來使用,所有的屬性和函數(shù)都是直接綁定到 _ 對(duì)象上面的,所有最終的調(diào)用都是通過:

_.each([22,33,44], console.log);
// 22 0 [22, 33, 44]
// 33 1 [22, 33, 44]
// 44 2 [22, 33, 44]

最終的返回值是處理的那個(gè)數(shù)組,而不是 _ 自己,下面將會(huì)討論,這個(gè)涉及到鏈?zhǔn)秸{(diào)用。

那如果,我就想通過函數(shù)來生成,這也是支持的:

_([1,2,3]).each(console.log)
// 返回的結(jié)果都是一樣的

這個(gè)時(shí)候,就會(huì)疑惑,_ 的原型呢?我們?cè)賮硭阉饕幌?_.prototype

_.mixin = function(obj) {
  _.each(_.functions(obj), function(name) { // 調(diào)用 each 對(duì)每一個(gè)函數(shù)對(duì)象處理
    var func = _[name] = obj[name]; // 綁定到 _ 上
    _.prototype[name] = function() { // 綁定到 _ 的原型上
      var args = [this._wrapped];
      push.apply(args, arguments); // 參數(shù)對(duì)齊
      return result(this, func.apply(_, args)); // 調(diào)用 result 查看是否鏈?zhǔn)?    };
  });
};

_.mixin(_); // 執(zhí)行

// 相關(guān)的一些方法
_.functions = _.methods = function(obj) {
  var names = [];
  for (var key in obj) {
    if (_.isFunction(obj[key])) names.push(key);
  }
  return names.sort();
};
_.isFunction = function(obj){
  return typeof obj == "function" || false;
}

_.functions 是一個(gè)獲取目標(biāo)所有函數(shù)對(duì)象的方法,并把這些方法淺拷貝傳遞給 __的原型,因?yàn)樵头椒?,處理?duì)象已經(jīng)在 _wrapped 中了,而這些常用的方法參數(shù)都是固定的,如果直接調(diào)用,參數(shù)會(huì)出問題,所以:

var args = [this._wrapped];
push.apply(args, arguments);// args 已經(jīng)拼接完成
func.apply(_, args);

那么 result 函數(shù)是用來做什么的?因?yàn)?underscore 有兩種調(diào)用方式,一種是通過 _.each(obj, func),另一種是通過 _(obj).each(func)。第一種方法很好理解,返回值要么是 obj 本身,要么是處理后的結(jié)果,而第二種調(diào)用方法和 jQuery 很像,先生成一個(gè) new 實(shí)體,對(duì)實(shí)體的進(jìn)行調(diào)用,也就有了上面的參數(shù)校準(zhǔn)問題。

不過這樣子又回帶來另一個(gè)問題,對(duì)于 each、map 函數(shù),函數(shù)返回什么不重要,主要是處理過程,可以支持鏈?zhǔn)秸{(diào)用,對(duì)于 reduce 函數(shù),返回的是處理后的結(jié)果,可以不用鏈?zhǔn)?,所?result 函數(shù)就是來判斷是否需要鏈?zhǔn)剑鴮?duì)返回值進(jìn)行處理。

介紹 result 之前,先來看一下 chain 函數(shù):

_.chain = function(obj) {
  var instance = _(obj);
  instance._chain = true; // 設(shè)置一個(gè) _chain 屬性,后面用于判斷鏈?zhǔn)?  return instance;
};

返回一個(gè)新的 _(obj),并且多了一個(gè) _chain 屬性,且為 true,所以 result 函數(shù):

var result = function(instance, obj) {
  return instance._chain ? _(obj).chain() : obj;
};

如果當(dāng)前是允許鏈?zhǔn)降?,可以進(jìn)行鏈?zhǔn)秸{(diào)用,不允許鏈?zhǔn)剑椭苯臃祷靥幚斫Y(jié)果,比如:

var arr = [22, 33, 44];
_.chain(arr)
  .map(function(v){ return v + 1 })
  .reduce(function(p, n){ return p + n }, 0)
  .value() // 102

// 如果不允許鏈?zhǔn)剑祷亟Y(jié)果是處理后的數(shù)組
_(arr)
  .map(function(v){ return v + 1 }) // [23, 34, 45]

現(xiàn)在返回來看一下 _ 函數(shù),也非常的有意思,_(obj)實(shí)際上是執(zhí)行兩次的,第二次才用到了 new:

var _ = function(obj) {
  if (obj instanceof _) return obj; // 如果 obj 繼承于 _,直接返回
  if (!(this instanceof _)) return new _(obj); // 如果 this 不繼承 _,返回一個(gè) new
  this._wrapped = obj; // 保存 obj 的值
};

現(xiàn)在應(yīng)該就非常的明朗了吧。當(dāng)調(diào)用 _([22,33,44]) 的時(shí)候,發(fā)現(xiàn) obj 并不是繼承與 _,會(huì)用 new 來生成,又會(huì)重新跑一遍 _ 函數(shù),然后將 _wrapped 屬性指向 obj。

由于在之前已經(jīng) root = this,Underscore 在不同的環(huán)境中都可以運(yùn)行,需要將 _ 放到不同的環(huán)境中:

if (typeof exports !== "undefined") { // nodejs 模塊
  if (typeof module !== "undefined" && module.exports) {
    exports = module.exports = _;
  }
  exports._ = _;
} else { // window
  root._ = _;
}
接著看源碼

源碼再往下看,是一個(gè) optimizeCb 函數(shù),用來優(yōu)化回調(diào)函數(shù):

var optimizeCb = function(func, context, argCount) {
  // 這里沒有用 undefined,而是用 void 0
  if (context === void 0) return func; // 只有一個(gè)參數(shù),直接返回回調(diào)函數(shù)
  switch (argCount == null ? 3 : argCount) { // call 比 apply 好?
    case 1: return function(value) {
      return func.call(context, value);
    };
    case 2: return function(value, other) {
      return func.call(context, value, other);
    };
    case 3: return function(value, index, collection) {
      return func.call(context, value, index, collection);
    };
    case 4: return function(accumulator, value, index, collection) {
      return func.call(context, accumulator, value, index, collection);
    };
  }
  // 最后走 apply 函數(shù)
  return function() {
    return func.apply(context, arguments);
  };
};

所謂優(yōu)化版的回調(diào)函數(shù),就是用 call 來固定參數(shù),1 個(gè)參數(shù),2 個(gè)參數(shù),3 個(gè)參數(shù),4 個(gè)參數(shù)的時(shí)候,由于 apply 可以不用考慮參數(shù),但是在性能上面貌似沒有 call 好。

然后后面還有一個(gè) cb 函數(shù),也是用來作為回調(diào)函數(shù)的。

var cb = function(value, context, argCount) {
  if (value == null) return _.identity;
  if (_.isFunction(value)) return optimizeCb(value, context, argCount);
  if (_.isObject(value)) return _.matcher(value);
  return _.property(value);
};
_.iteratee = function(value, context) {
  return cb(value, context, Infinity);
};

iteratee 可以用來對(duì)函數(shù)進(jìn)行處理,給一個(gè)函數(shù)綁定 this 等等,最總還是調(diào)用到 cb,其實(shí) cb 本身就很復(fù)雜,要么是一個(gè) identity 函數(shù),要么是一個(gè)優(yōu)化到回調(diào)函數(shù),要么是一個(gè) property 獲取屬性函數(shù)。

再往下就是 createAssigner,搜了一下,發(fā)現(xiàn)全文有三處用到此函數(shù),分別是 extend、extendOwn、default,可以看出來,此函數(shù)主要到作用是用來實(shí)現(xiàn)拷貝,算是拷貝到輔助函數(shù)吧,把拷貝公共到部分抽離出來:

var createAssigner = function(keysFunc, undefinedOnly) {
  return function(obj) {
    var length = arguments.length;
    if (length < 2 || obj == null) return obj;

    // 將第二個(gè)參數(shù)及以后的 object 拷貝到第一個(gè) obj 上
    for (var index = 1; index < length; index++) {
      var source = arguments[index],
          // keysFunc 是點(diǎn)睛所在
          // 不同的 keysFunc 獲得的 keys 集合不同
          // 分為兩種,所有 keys(包括繼承),自身 keys
          keys = keysFunc(source),
          l = keys.length;
      for (var i = 0; i < l; i++) {
        var key = keys[i];
        // underfinedOnly 表示是否覆蓋原有
        if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
      }
    }
    return obj;
  };
};

所以當(dāng) keyFunc 函數(shù)獲得所有 keys 時(shí),包括繼承來的,這個(gè)時(shí)候就對(duì)應(yīng)于 _.extend 函數(shù),非繼承 keys 時(shí),對(duì)應(yīng)于 _.extendOwn。如果 underfinedOnly 設(shè)置為 true,則實(shí)現(xiàn)的是不替換原有屬性的繼承 _.defaults。

在 Underscore 中,原型的繼承用 baseCreate 函數(shù):

var Ctor = function(){};

var baseCreate = function(prototype) {
  if (!_.isObject(prototype)) return {};
  if (nativeCreate) return nativeCreate(prototype);
  Ctor.prototype = prototype;
  var result = new Ctor;
  Ctor.prototype = null;
  return result;
};

nativeCreate 之前已經(jīng)介紹來,就是 Object.create,所以,如果瀏覽器不支持,下面實(shí)現(xiàn)的功能就是在實(shí)現(xiàn)這個(gè)函數(shù),方法也很常規(guī),用了一個(gè)空函數(shù) Ctor 主要是防止 new 帶來的多余屬性問題。

property 函數(shù)也是一個(gè)比較有意思的函數(shù),使用了閉包的思路,比如判斷一個(gè)對(duì)象是否為類似數(shù)組結(jié)構(gòu)的時(shí)候就用到了這個(gè)函數(shù):

var property = function(key) {
  return function(obj) {
    return obj == null ? void 0 : obj[key];
  };
};

var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = property("length"); // 返回一個(gè)閉包韓式,用來檢測(cè)對(duì)象是非有 length 參數(shù)
var isArrayLike = function(collection) {
  var length = getLength(collection);
  return typeof length == "number" && length >= 0 && length <= MAX_ARRAY_INDEX;
};

而且我搜索了一下,發(fā)現(xiàn) getLength 函數(shù)使用的地方還是挺多的。

總結(jié)

總的來說,這些開源的庫,都保持著自己的一種風(fēng)格,jQuery 是這樣,Underscore 也是這樣,從 Underscore 的總體架構(gòu)可以發(fā)現(xiàn),它主要封裝了一些好用的方法。

參考

Underscore.js (1.8.3) 中文文檔
Underscore源碼解析(一)
中文版 underscore 代碼注釋

歡迎來我的博客交流。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/82749.html

相關(guān)文章

  • underscore源碼剖析之整體架構(gòu)

    摘要:我這里有個(gè)不夠準(zhǔn)確但容易理解的說法,就是檢查一個(gè)對(duì)象是否為另一個(gè)構(gòu)造函數(shù)的實(shí)例,為了更容易理解,下面將全部以是的實(shí)例的方式來說。 underscore源碼分析之整體架構(gòu) 最近打算好好看看underscore源碼,一個(gè)是因?yàn)樽约捍_實(shí)水平不夠,另一個(gè)是underscore源碼比較簡單,比較易讀。本系列打算對(duì)underscore1.8.3中關(guān)鍵函數(shù)源碼進(jìn)行分析,希望做到最詳細(xì)的源碼分析。今...

    2shou 評(píng)論0 收藏0
  • 學(xué)習(xí) underscore 源碼整體架構(gòu),打造屬于自己的函數(shù)式編程類庫

    摘要:譯立即執(zhí)行函數(shù)表達(dá)式處理支持瀏覽器環(huán)境微信小程序。學(xué)習(xí)整體架構(gòu),利于打造屬于自己的函數(shù)式編程類庫。下一篇文章可能是學(xué)習(xí)的源碼整體架構(gòu)。也可以加微信,注明來源,拉您進(jìn)前端視野交流群。 前言 上一篇文章寫了jQuery整體架構(gòu),學(xué)習(xí) jQuery 源碼整體架構(gòu),打造屬于自己的 js 類庫 雖然看過挺多underscore.js分析類的文章,但總感覺少點(diǎn)什么。這也許就是紙上得來終覺淺,絕知此...

    junnplus 評(píng)論0 收藏0
  • Underscore 整體架構(gòu)淺析

    摘要:支持形式的調(diào)用這其實(shí)是非常經(jīng)典的無構(gòu)造,其實(shí)就是一個(gè)構(gòu)造函數(shù),的結(jié)果就是一個(gè)對(duì)象實(shí)例,該實(shí)例有個(gè)屬性,屬性值是。 前言 終于,樓主的「Underscore 源碼解讀系列」underscore-analysis 即將進(jìn)入尾聲,關(guān)注下 timeline 會(huì)發(fā)現(xiàn)樓主最近加快了解讀速度。十一月,多事之秋,最近好多事情搞的樓主心力憔悴,身心俱疲,也想盡快把這個(gè)系列完結(jié)掉,也好了卻一件心事。 本文...

    ningwang 評(píng)論0 收藏0
  • underscore源碼學(xué)習(xí)(

    摘要:所以,剛開始,我從源碼比較短的包含注釋只有行開始學(xué)習(xí)起。一般,在客戶端瀏覽器環(huán)境中,即為,暴露在全局中。學(xué)習(xí)以后判斷直接使用看起來也優(yōu)雅一點(diǎn)滑稽臉。在的函數(shù)視線中,的作用執(zhí)行一個(gè)傳入函數(shù)次,并返回由每次執(zhí)行結(jié)果組成的數(shù)組。 前言 最近在社區(qū)瀏覽文章的時(shí)候,看到了一位大四學(xué)長在尋求前端工作中的面經(jīng),看完不得不佩服,掌握知識(shí)點(diǎn)真是全面,無論是前端后臺(tái)還是其他,都有涉獵。 在他寫的文章中,有...

    gclove 評(píng)論0 收藏0
  • underscore源碼分析之基礎(chǔ)方法

    摘要:在上篇文章整體架構(gòu)分析中,我們講過上面的方法有兩種掛載方式,一個(gè)是掛載到構(gòu)造函數(shù)上以的形式直接調(diào)用在后文上統(tǒng)稱構(gòu)造函數(shù)調(diào)用,另一種則是掛到上以的形式被實(shí)例調(diào)用在后文上統(tǒng)稱原型調(diào)用。 underscore源碼分析之基礎(chǔ)方法 本文是underscore源碼剖析系列的第二篇,主要介紹underscore中一些基礎(chǔ)方法的實(shí)現(xiàn)。 mixin 在上篇文章underscore整體架構(gòu)分析中,我們講...

    BigNerdCoding 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<