我看underscore 源碼設(shè)計

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

構(gòu)造函數(shù)

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

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

_是一個函數(shù)對象,api中的函數(shù)全都掛載到_上,實(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í)例,就直接返回該參數(shù)  
        if (obj instanceof _) return obj;
        //  對應(yīng)_(xxx)調(diào)用,如果new實(shí)例不是_的實(shí)例,就返回實(shí)例化_對象
        if (!(this instanceof _)) return new _(obj);
        // 并將數(shù)據(jù)對象包裝在實(shí)例對象上 
        this._wrapped = obj;
     };
     // 省略...
     
     //注冊在全局對象
     root._=_; 
    
})();    

mixin

上一步中,我們創(chuàng)建了underscore實(shí)例,只能支持_.func調(diào)用,如果要支持_(obj).func,同時還要將func注冊在實(shí)例的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. 獲取當(dāng)前實(shí)例上注冊的函數(shù)數(shù)組
  2. 遍歷數(shù)組將函數(shù)注冊在實(shí)例原型上
  3. _(args).func(argument)參數(shù)進(jìn)行合并

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

鏈?zhǔn)秸{(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)用當(dāng)前函數(shù)的對象,以保證可以調(diào)用下個函數(shù)

可能會想到,在每個函數(shù)結(jié)尾return this;,對,能用但是沒必要,除了要手動添加外,我們也無法關(guān)閉鏈?zhǔn)健?/p>

underscore中,使用的是可選式實(shí)現(xiàn)鏈接編程,使用.chain()來開啟鏈?zhǔn)秸{(diào)用,然后使用.value()獲取函數(shù)的最終值。

關(guān)鍵函數(shù):

// 開啟鏈?zhǔn)秸{(diào)用
_.chain = function (obj) { 
    //返回一個封裝的對象. 在封裝的對象上調(diào)用方法會返回封裝的對象本身
    var instance = _(obj)
    instance._chain = true //檢測對象是否支持鏈?zhǔn)秸{(diào)用
    return instance
}
// 輔助函數(shù):鏈?zhǔn)街修D(zhuǎn)
// 鏈?zhǔn)秸{(diào)用 將數(shù)據(jù)對象封裝在underscore實(shí)例中
// 非鏈?zhǔn)秸{(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;
};

總結(jié)

此次學(xué)習(xí)目標(biāo)不是為了學(xué)習(xí)api,而是通過將其設(shè)計思想轉(zhuǎn)換為自己的。通過以上幾點(diǎn),我們可以大概實(shí)現(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í)行鏈?zhǔn)讲僮?  _.chain = function (obj) { //數(shù)據(jù)源
    var instance = _(obj)
    instance._chain = true //檢測對象是否支持鏈?zhǔn)秸{(diào)用
    return instance
  }
  //輔助函數(shù) 將數(shù)據(jù)包裝為underscore實(shí)例
  var ret = function (instance, obj) {
    if (instance._chain) {
      instance.warp = obj
      return instance
    }
    return obj
  }

  _.map1 = function (obj) {
    obj.push('123', 'hello')
    return obj
  }
  // 關(guān)閉鏈?zhǔn)秸{(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進(jìn)行注冊
  _.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
        // 進(jìn)行參數(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())
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,702評論 2 370

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