年糕閱讀源碼:Underscore.js(1)

前言

Underscore.js是一款精簡(jiǎn)但是對(duì)很多常用功能進(jìn)行了封裝的JavaScript框架(英文文檔:http://underscorejs.org/肺樟,中文文檔:http://www.css88.com/doc/underscore/),整個(gè)篇幅比較短新症,所以我覺得很適合我這種前端菜鳥來閱讀吧择示。希望我能堅(jiān)持閱讀下去,并致力于弄懂。

一覽

可以看到外面是直接調(diào)用匿名匿名函數(shù)榛丢,相當(dāng)于function noName{...};noName();因?yàn)樗鼤?huì)在里面定義到一些變量,而我們一般是用它內(nèi)部定義的方法去訪問挺庞,所以就會(huì)存在一個(gè)閉包晰赞。

(function() {
  // 內(nèi)容
}())

滿滿的下綴_

可以看到代碼中""這個(gè)符號(hào)出現(xiàn)的頻率非常之高,可以查看中文文檔选侨,然后會(huì)發(fā)現(xiàn)它里面的方法前綴都是""來標(biāo)記的掖鱼。其實(shí)underscore這個(gè)名字的意思也是"_"。

預(yù)處理篇

定義根

下面我們來看代碼援制,第一句是定義了root變量戏挡。root變量就是某個(gè)環(huán)境夏的最頂端變量,也是原型鏈的自頂端隘谣。在瀏覽器中是window增拥,在node環(huán)境中是global,所以這里有四種判斷情況寻歧。
聯(lián)想提示:知道shim和polyfill的區(qū)別嗎?
是為了能在低ES版本下實(shí)現(xiàn)高ES版本所以寫的一系列代碼掌栅,其實(shí)shim和polyfill都是這個(gè)意思,但是它們的區(qū)別是polyfill是特質(zhì)瀏覽器中的shim码泛,而shim則支持多個(gè)平臺(tái)猾封,會(huì)讓我想到這個(gè)是因?yàn)檫@個(gè)定義也看得出underscore不僅支持瀏覽器,還支持Node環(huán)境噪珊。

  var root = typeof self == 'object' && self.self === self && self ||
            typeof global == 'object' && global.global === global && global ||
            this ||
            {};

Q:self是什么東西呀晌缘?
A:在瀏覽器中測(cè)試可知,這個(gè)self其實(shí)就是window痢站。而self.self指向的也同樣是window磷箕。

Q:為什么使用self.self和global.global能判斷它就是window或者global呢?

保存會(huì)用到的變量方法

  //保存原來的下劃線變量意味
  var previousUnderscore = root._;

  // 數(shù)組原型阵难、對(duì)象原型岳枷、Symbol原型(如果支持Symbol)
  var ArrayProto = Array.prototype, ObjProto = Object.prototype;
  var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;

  // 保存數(shù)組的push、slice呜叫、toString方法空繁,保存Object的hasOwnProperty方法
  var push = ArrayProto.push,
      slice = ArrayProto.slice,
      toString = ObjProto.toString,
      hasOwnProperty = ObjProto.hasOwnProperty;

  // 在ES5中會(huì)使用到的方法:判斷是否為數(shù)組,返回鍵值組成的數(shù)組朱庆,創(chuàng)建一個(gè)對(duì)象
  var nativeIsArray = Array.isArray,
      nativeKeys = Object.keys,
      nativeCreate = Object.create;

擴(kuò)展回憶:
使用Object.create(原型)盛泡,如果填入的參數(shù)為null,就可以創(chuàng)建一個(gè)沒有原型的對(duì)象娱颊。

處理參數(shù)和函數(shù)

var Ctor = function(){};

定義一個(gè)空殼函數(shù)Ctor來方便做原型的替代

  var _ = function(obj) {
    if (obj instanceof _) return obj;
    if (!(this instanceof _)) return new _(obj);
    this._wrapped = obj;
  };

定義我們自己的_變量傲诵,是一個(gè)可以傳入一個(gè)obj的函數(shù)凯砍。如果這個(gè)obj是的實(shí)例,就返回這個(gè)obj掰吕。如果不是的話就new一個(gè)(obj)返回果覆,new操作會(huì)把新對(duì)象的原型指向_,this會(huì)指向新對(duì)象殖熟,并且會(huì)執(zhí)行this.wrapped = obj局待。所以執(zhí)行這個(gè)函數(shù)的話,會(huì)得到一個(gè)類似于下圖的結(jié)構(gòu):

new _(obj)得到的實(shí)例結(jié)構(gòu)

所以這個(gè)對(duì)象就成為了_的一個(gè)實(shí)例菱属。

下面是對(duì)于在node環(huán)境中的處理此處還沒找到實(shí)驗(yàn)環(huán)境钳榨,先跳過

  if (typeof exports != 'undefined' && !exports.nodeType) {
    if (typeof module != 'undefined' && !module.nodeType && module.exports) {
      exports = module.exports = _;
    }
    exports._ = _;
  } else {
    root._ = _;
  }

指明Undersocre.js的版本,這里可以看到我下載的源碼是1.9.0版本的纽门。

_.VERSION = '1.9.0';

處理參數(shù)

func:函數(shù)
context:上下文
argCount:參數(shù)個(gè)數(shù)
這里直接看其實(shí)也感覺很難理解薛耻,所以我會(huì)先在后面提取一個(gè)例子,結(jié)合文檔的用法來搞清楚這個(gè)函數(shù)到底的作用赏陵。

  var optimizeCb = function(func, context, argCount) {
    if (context === void 0) return func;
   //如果沒有傳入argCount饼齿,進(jìn)入3,否則對(duì)應(yīng)進(jìn)入不同的參數(shù)
    switch (argCount == null ? 3 : argCount) {
      // 返回一個(gè)直接傳入value的函數(shù)
      case 1: return function(value) {
        return func.call(context, value);
      };
      // 兩個(gè)參數(shù)的情況省略了蝙搔,因?yàn)槲覀儾粫?huì)用到
      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);
      };
    }
    return function() {
      return func.apply(context, arguments);
    };
  };
  1. context === void 0是什么意思呀缕溉?

    關(guān)于void 0

    這是在stackOverflow上找到的一個(gè)回答,void 0可以和undefined有同樣的作用吃型,但是它的字節(jié)要少一些所以用了它來減少尺寸哈哈哈证鸥。

  2. 這里使用了argCount == null,argCount如果沒有傳入實(shí)際上是undefined勤晚,但是undefined == null枉层,所以沒有傳入argCount會(huì)直接進(jìn)行3的分支。

一個(gè)內(nèi)置的函數(shù)來生成能調(diào)用多次調(diào)用回調(diào)函數(shù):

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

  _.iteratee = builtinIteratee = function(value, context) {
    return cb(value, context, Infinity);
  };

處理剩余傳入的多個(gè)參數(shù)赐写,化為一個(gè)數(shù)組鸟蜡,類似于ES6中的...rest:

  var restArguments = function(func, startIndex) {
    startIndex = startIndex == null ? func.length - 1 : +startIndex;
    return function() {
      var length = Math.max(arguments.length - startIndex, 0),
          rest = Array(length),
          index = 0;
      for (; index < length; index++) {
        rest[index] = arguments[index + startIndex];
      }
      switch (startIndex) {
        case 0: return func.call(this, rest);
        case 1: return func.call(this, arguments[0], rest);
        case 2: return func.call(this, arguments[0], arguments[1], rest);
      }
      var args = Array(startIndex + 1);
      for (index = 0; index < startIndex; index++) {
        args[index] = arguments[index];
      }
      args[startIndex] = rest;
      return func.apply(this, args);
    };
  };

創(chuàng)建一個(gè)對(duì)象

  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;
  };

返回對(duì)象的屬性值(淺返回)

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

按照path數(shù)組,返回一組屬性

  var deepGet = function(obj, path) {
    var length = path.length;
    for (var i = 0; i < length; i++) {
      if (obj == null) return void 0;
      obj = obj[path[i]];
    }
    return length ? obj : void 0;
  };

幫助集合來定義它是否為一個(gè)集合挺邀。應(yīng)該以數(shù)組/對(duì)象的方式來循環(huán)嗎揉忘?

 //數(shù)組索引最大值
  var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
//獲取length值
  var getLength = shallowProperty('length');
//是不是類數(shù)組?
  var isArrayLike = function(collection) {
    var length = getLength(collection);
    return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
  };
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末悠夯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子躺坟,更是在濱河造成了極大的恐慌沦补,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咪橙,死亡現(xiàn)場(chǎng)離奇詭異夕膀,居然都是意外死亡虚倒,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門产舞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來魂奥,“玉大人,你說我怎么就攤上這事易猫〕苊海” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵准颓,是天一觀的道長(zhǎng)哈蝇。 經(jīng)常有香客問我,道長(zhǎng)攘已,這世上最難降的妖魔是什么炮赦? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮样勃,結(jié)果婚禮上吠勘,老公的妹妹穿的比我還像新娘。我一直安慰自己峡眶,他們只是感情好剧防,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著幌陕,像睡著了一般诵姜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上搏熄,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天棚唆,我揣著相機(jī)與錄音,去河邊找鬼心例。 笑死宵凌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的止后。 我是一名探鬼主播瞎惫,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼译株!你這毒婦竟也來了瓜喇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤歉糜,失蹤者是張志新(化名)和其女友劉穎乘寒,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體匪补,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伞辛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年烂翰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚤氏。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡甘耿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出竿滨,到底是詐尸還是另有隱情佳恬,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布姐呐,位于F島的核電站殿怜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏曙砂。R本人自食惡果不足惜头谜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鸠澈。 院中可真熱鬧柱告,春花似錦、人聲如沸笑陈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涵妥。三九已至乖菱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蓬网,已是汗流浹背窒所。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帆锋,地道東北人吵取。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像锯厢,于是被迫代替她去往敵國和親皮官。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容