我看underscore 源碼設計

先過濾掉underscore內(nèi)部各個工具函數(shù)的具體邏輯崭歧,只看源碼庫本身有什么內(nèi)容隅很。

構造函數(shù)

underscore有兩種調(diào)用方式:

  1. 風格對象 _.map([1, 2, 3], function(n){ return n * 2; });
  2. 函數(shù)風格_([1, 2, 3]).map(function(n){ return n * 2; });

_是一個函數(shù)對象,api中的函數(shù)全都掛載到_上率碾,實現(xiàn)_.func

// 使用立即執(zhí)行函數(shù)
(function () {
      // 定義全局對象
      var root = typeof self == 'object' && self.self === self && self ||
            typeof global == 'object' && global.global === global && global ||
            this ||
            {};
     // 省略...
     // 創(chuàng)建_對象
     var _ = function(obj) {
        // 如果參數(shù)是underscore的一個實例叔营,就直接返回該參數(shù)  
        if (obj instanceof _) return obj;
        //  對應_(xxx)調(diào)用,如果new實例不是_的實例所宰,就返回實例化_對象
        if (!(this instanceof _)) return new _(obj);
        // 并將數(shù)據(jù)對象包裝在實例對象上 
        this._wrapped = obj;
     };
     // 省略...
     
     //注冊在全局對象
     root._=_; 
    
})();    

mixin

上一步中绒尊,我們創(chuàng)建了underscore實例,只能支持_.func調(diào)用仔粥,如果要支持_(obj).func婴谱,同時還要將func注冊在實例的prototype上。試想下躯泰,如果每聲明一個函數(shù)谭羔,就要綁定一次,那得用多…

在underscore中斟冕,使用mixin將自定義函數(shù)添加到Underscore對象口糕。

// 用于返回排序后的數(shù)組,包含所有的obj中的函數(shù)名磕蛇。
_.functions = _.methods = function(obj) {
    var names = [];
    for (var key in obj) {
      if (_.isFunction(obj[key])) names.push(key);
    }
    return names.sort();
};
_.mixin = function(obj) {
    // 遍歷obj的函數(shù)景描,綁定在原型上
    _.each(_.functions(obj), function(name) {  
      var func = _[name] = obj[name];
      _.prototype[name] = function() {
        // this._wrapped作為第一個參數(shù)傳遞十办,其他用戶傳遞的參數(shù)放在后面。  
        var args = [this._wrapped];
        push.apply(args, arguments);
        return chainResult(this, func.apply(_, args));
      };
    });
    return _;
};
// 執(zhí)行混入
 _.mixin(_);

大致流程是:

  1. 獲取當前實例上注冊的函數(shù)數(shù)組
  2. 遍歷數(shù)組將函數(shù)注冊在實例原型上
  3. _(args).func(argument)參數(shù)進行合并

_自身定義一系列函數(shù)超棺,通過_.mixin()綁定在了_.prototype上向族,提高了代碼的復用度。

鏈式調(diào)用

類似于 Java Stream 流式編程

1584758293868.png

在Javascript中棠绘,數(shù)據(jù)可以像是在管道中流通件相,我們稱之為,聲明式編程/鏈接式調(diào)用氧苍。

data.filter(...).unique(...).map(...)

既然要滿足鏈接式調(diào)用(Chaining)語法夜矗,需要滿足兩個條件

  1. 前一次調(diào)用返回數(shù)據(jù)對象,用來給下一個函數(shù)調(diào)用提供數(shù)據(jù)來源
  2. 返回調(diào)用當前函數(shù)的對象让虐,以保證可以調(diào)用下個函數(shù)

可能會想到紊撕,在每個函數(shù)結尾return this;,對赡突,能用但是沒必要对扶,除了要手動添加外,我們也無法關閉鏈式惭缰。

underscore中浪南,使用的是可選式實現(xiàn)鏈接編程,使用.chain()來開啟鏈式調(diào)用漱受,然后使用.value()獲取函數(shù)的最終值络凿。

關鍵函數(shù):

// 開啟鏈式調(diào)用
_.chain = function (obj) { 
    //返回一個封裝的對象. 在封裝的對象上調(diào)用方法會返回封裝的對象本身
    var instance = _(obj)
    instance._chain = true //檢測對象是否支持鏈式調(diào)用
    return instance
}
// 輔助函數(shù):鏈式中轉(zhuǎn)
// 鏈式調(diào)用 將數(shù)據(jù)對象封裝在underscore實例中
// 非鏈式調(diào)用 返回數(shù)據(jù)對象
var chainResult = function(instance, obj) {
    return instance._chain ? _(obj).chain() : obj;
};
 var _ = function(obj) {
    if (obj instanceof _) return obj;
    if (!(this instanceof _)) return new _(obj);
    this._wrapped = obj;
  };
_.prototype.value = function() {
    return this._wrapped;
};

總結

此次學習目標不是為了學習api,而是通過將其設計思想轉(zhuǎn)換為自己的昂羡。通過以上幾點喷众,我們可以大概實現(xiàn)一個簡化版的underscore,

簡化版:

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

  }
  _.unique = function (arr, callback) {
    var result = []
    var item
    for (var i = 0; i < arr.length; i++) {
      item = callback ? callback(arr[i]) : arr[i]
      if (result.indexOf(item) === -1) {
        result.push(item)
      }
    }
    return result
  }
  // 獲取對象上的函數(shù)
  _.functions = function (obj) {
    var result = []
    for (let key in obj) {
      result.push(key)
    }
    return result
  }
  // 執(zhí)行鏈式操作
  _.chain = function (obj) { //數(shù)據(jù)源
    var instance = _(obj)
    instance._chain = true //檢測對象是否支持鏈式調(diào)用
    return instance
  }
  //輔助函數(shù) 將數(shù)據(jù)包裝為underscore實例
  var ret = function (instance, obj) {
    if (instance._chain) {
      instance.warp = obj
      return instance
    }
    return obj
  }

  _.map1 = function (obj) {
    obj.push('123', 'hello')
    return obj
  }
  // 關閉鏈式調(diào)用 返回數(shù)據(jù)本身
  _.prototype.value = function () {
    return this.warp
  }
  _.each = function (arr, callback) {
    var i = 0
    for (; i < arr.length; i++) {
      callback.call(arr, arr[i])
    }
    //console.log(arr)
  }
  // 檢測靜態(tài)方法 name 存放在數(shù)組中
  // 遍歷數(shù)組 給_.prototype進行注冊
  _.mixin = function (obj) {
    _.each(_.functions(obj), function (key) {
      var func = obj[key]
      //console.log(key)
      _.prototype[key] = function () {
        //console.log(this.warp) //數(shù)據(jù)源
        //console.log(arguments) //callback
        // 進行參數(shù)合并
        var args = [this.warp]
        Array.prototype.push.apply(args, arguments)
        return ret(this, func.apply(this, args))
      }
    })
  }
  _.mixin(_)
  root._ = _
})(this)

調(diào)用:

    console.log(_.unique([1,2,3,1,2,3,'a','A'],function (item) {
        // 過濾大小寫
        return typeof item ==='string'?item.toLowerCase():item
    }))
    console.log(_([4,5,6,4,5,6,'b','B']).chain().unique(function (item) {
      // 過濾大小寫
      return typeof item ==='string'?item.toLowerCase():item
    }).map1().value())
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末紧憾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子昌渤,更是在濱河造成了極大的恐慌赴穗,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膀息,死亡現(xiàn)場離奇詭異般眉,居然都是意外死亡,警方通過查閱死者的電腦和手機潜支,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門甸赃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人冗酿,你說我怎么就攤上這事埠对÷缍希” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵项玛,是天一觀的道長貌笨。 經(jīng)常有香客問我,道長襟沮,這世上最難降的妖魔是什么锥惋? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮开伏,結果婚禮上膀跌,老公的妹妹穿的比我還像新娘。我一直安慰自己固灵,他們只是感情好捅伤,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著怎虫,像睡著了一般暑认。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上大审,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天蘸际,我揣著相機與錄音,去河邊找鬼徒扶。 笑死粮彤,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的姜骡。 我是一名探鬼主播导坟,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼圈澈!你這毒婦竟也來了惫周?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤康栈,失蹤者是張志新(化名)和其女友劉穎递递,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體啥么,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡登舞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了悬荣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菠秒。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖氯迂,靈堂內(nèi)的尸體忽然破棺而出践叠,到底是詐尸還是另有隱情言缤,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布酵熙,位于F島的核電站轧简,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏匾二。R本人自食惡果不足惜哮独,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望察藐。 院中可真熱鬧皮璧,春花似錦、人聲如沸分飞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽譬猫。三九已至讯檐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間染服,已是汗流浹背别洪。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留柳刮,地道東北人挖垛。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像秉颗,于是被迫代替她去往敵國和親痢毒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

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