underscore.js源碼研究(8)

概述

很早就想研究underscore源碼了,雖然underscore.js這個庫有些過期了,可是我仍是想學習一下庫的架構,函數式編程以及經常使用方法的編寫這些方面的內容,又剛好沒什麼其它要研究的了,因此就告終研究underscore源碼這一心願吧。html

underscore.js源碼研究(1)
underscore.js源碼研究(2)
underscore.js源碼研究(3)
underscore.js源碼研究(4)
underscore.js源碼研究(5)
underscore.js源碼研究(6)
underscore.js源碼研究(7)
underscore.js源碼研究(8)git

參考資料:underscore.js官方註釋undersercore 源碼分析undersercore 源碼分析 segmentfault編程

鏈式調用

對於一個對象的方法,咱們能夠在方法裏面return this來使它支持鏈式調用。若是不修改原方法的源碼,咱們能夠經過下面的函數使它支持鏈式調用。segmentfault

//其中obj是一個對象,functions是對象的方法名數組
function chained(obj, functions) {
    //因爲functions是一個數組,因此可使用foreach
    functions.forEach((funcName) => {
        const func = obj[funcName];
        //修改原方法
        obj[funcName] = function() {
            func.apply(this, arguments);
            return this;
        }
    })
}

示例以下:api

let speaker = {
    haha() {
        console.log('haha');
    },
    yaya() {
        console.log('yaya');
    },
    lala() {
        console.log('lala');
    }
}

chained(speaker, ['haha', 'lala', 'yaya']);
speaker.haha().yaya().haha().lala().yaya();

輸出以下:數組

haha
yaya
haha
lala
yaya

underscore.js裏面的鏈式調用

underscore.js裏面的鏈式調用與上面的稍微有些不一樣。架構

它的機制是這樣的:app

  1. 把underscore.js的方法所有掛載到_下面。
  2. 利用_.function方法遍歷全部掛載在_下面的方法,而後掛載到_.prototype下面,這樣生成的underscore對象就能夠直接調用這些方法了。
  3. 在把方法掛載到_.prototype下面的時候,會利用相似上面的函數對方法進行改寫(增長return this)。
  4. 在改寫的時候創建一個標識_chain,來標識這個方法能不能鏈式調用。

下面來具體看看是怎麼運做的:函數式編程

首先,利用下面的函數,能夠獲得一個數組,這個數組裏面全是掛載到_下面的方法名字函數

_function = _.methods = function(obj) {
    var names = [];
    for( var key in obj) {
        if(_.isFunction(obj[key])) names.push(key);
    }
    return names.sort();
};

而後咱們對每個方法名字,把它掛載到_.prototype下面

_.mixin = function(obj) {
    _.each(_.functions(obj), function(name) {
        var func = _[name] = obj[name];
        _.prototype[name] = function() {
            var args = [this._wrapped];
            push.apply(args, arguments);
            return chainResult(this, func.apply(_, args));
        };
    });
};
_.mixin(_);

從上面能夠看到,在掛載的時候返回一個chainResult方法處理過的東西。而args就等於原對象+參數1+參數2+...組成的數組,因此func.apply(_, args)就是_[func](原對象,參數1,參數2,...)處理後的結果

再來看看chainResult是怎麼樣的:

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

它判斷若是原對象可以鏈式調用,那麼處理後的結果obj也可以鏈式調用。怎麼讓結果也能鏈式調用呢?答案是使用_.chain方法:

_chain = function(obj) {
    var instance = _(obj);
    instance._chain = true;
    return instance;
}

這個方法和咱們最開始的chained方法差很少,可是它會把原對象轉化爲underscore對象,而且這個對象的**_chain屬性爲真**,即它可以被鏈式調用。

因此若是咱們要在underscore.js中實現鏈式調用的話,直接用chain方法便可,實例以下:

_([1,2]).push(3).push(5) //輸出4,並非咱們想要的
_([1,2]).chain().push(3).push(5).value() //輸出[1, 2, 3, 5],正是咱們想要的

能夠看到,咱們上面用到了value()函數,它可以中斷鏈式調用,並不返回指針而是返回值,代碼以下:

_.prototype.value = function() {
    return this._wrapped;
}

等等,_wrapped又是什麼?它是生成underscore對象前的原對象,看下面的代碼:

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

也就是說,在使用_生成underscore對象的時候,把原對象儲存在**_wrapped屬性**裏面了,因此_wrapped就是原對象。

相關文章
相關標籤/搜索