Underscore整體概述

1. 作用域

與其他第三方庫一樣,Underscore也通過立即執(zhí)行函數(shù)來包裹自己的業(yè)務(wù)邏輯。

目的:

  • 避免全局污染:所有庫中的邏輯,庫所定義和使用的變量全部被封裝到了該函數(shù)的作用域中。
  • 隱私保護(hù):但凡在立即執(zhí)行函數(shù)中聲明的函數(shù)、變量等,除非是自己想暴露,否則絕無可能在外部獲得。
(function(root){
  var _ = function(){
    
  }
  root._ = _
})(this)

2. _ 對象

underscore有下劃線的意思,所以underscore通過一個下劃線變量 _ 來標(biāo)識自身。

注: _ 是一個函數(shù)對象,之后所有的api都會被掛載到這個對象上,例如_.map()

(funtion(root){
  var _ = function(obj){
   
  }
  _.map = function(){
    //balalala
  }
  // ......
  root._ = _
})(this)

3. _()

雖然Underscore推崇函數(shù)式編程,但也支持面向?qū)ο箫L(fēng)格的函數(shù)調(diào)用,僅需要通過_()來包裹對象即可。
當(dāng)我們進(jìn)行如下調(diào)用時:_([1,2,3])會創(chuàng)建一個新的underscore對象實例(從而能夠調(diào)用underscore提供的方法),并在this._wrapped中存儲傳入的數(shù)據(jù)。

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

當(dāng)使用面向?qū)ο蟮姆绞秸{(diào)用時,例如傳入[1,2,3],函數(shù)首先會判斷是不是_的實例,如果是直接返回這個實例對象,如果不是則創(chuàng)建一個實例對象new _(obj),然后將參數(shù)[1,2,3]賦值給_wrapped,以備后用。

4. mixin

  • mixin(混入)模式是增加代碼復(fù)用度的一個廣泛的設(shè)計模式。
  • _.mixin(obj):為underscore對象混入obj具有的功能
  • mixin主要實現(xiàn)了 _ 的原型對象的方法調(diào)用

源碼如下:

_.mixin = function(obj) {
    _.each(_.functions(obj), function(name) {
        var func = obj[name];

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

最后一句是執(zhí)行這個mixin函數(shù),執(zhí)行完后會將 _ 上掛載的方法都掛載到原型對象prototype上,以方便面向?qū)ο髸r的調(diào)用

5. 鏈?zhǔn)秸{(diào)用 _.chain()

  • _.chain(obj):為underscore對象的方法增加鏈?zhǔn)秸{(diào)用能力
  • _.chain源碼如下:
_.chain = function(obj){
  var instance = _(obj)
  instance._chain = true
  return instance
}

比較簡單,就是拿到這個實例對象,賦了一個屬性 _chain為true,再返回這個實例對象

  • underscore還提供了一個幫助函數(shù)result,該函數(shù)將會判斷方法調(diào)用結(jié)果,如果該方法的調(diào)用者被標(biāo)識了需要鏈化,則鏈化當(dāng)前的方法執(zhí)行結(jié)果。
  • 源碼如下:
var chainResult = function(instance , obj){
  return instance._chain ? _(obj).chain() : obj ;  
}

============這是分割線============


接下來我們就看看,當(dāng)調(diào)用_.unique([1,2,3,1])的時候怎么執(zhí)行的
先附上源碼:

(function(root){
  var _ = function(){}
  
  _.unique = function(arr,callback){
    var ret = [];
    var target, i = 0;
    for (; i < arr.length; i++) {
        var target = callback ? callback(arr[i]) : arr[i];
        if (ret.indexOf(target) === -1) {
            ret.push(target);
        }
    }
    return ret;
  }

  root._ = _
})(this)

代碼就不解釋了,比較簡單,就是在 _ 對象上新建一個unique方法直接調(diào)用就行。

如果使用面向?qū)ο蟮恼{(diào)用方式呢?就需要這樣寫了 _([1,2,3,1]).chain().unique()
先附上源碼:

(function(root){
  var push = Array.prototype.push;
  //面向?qū)ο笳{(diào)用時返回_的實例,然后將參數(shù)存儲在_wrapped中
  var _ = function(obj) {
    if (obj instanceof _) {
        return obj;
    }
    if (!(this instanceof _)) {
        return new _(obj);
    }
    this._wrapped = obj;
  }
  //給_定義一個去重的函數(shù)
  _.unique = function(arr, callback) {
    var ret = [];
    var target, i = 0;
    for (; i < arr.length; i++) {
        var target = callback ? callback(arr[i]) : arr[i];
        if (ret.indexOf(target) === -1) {
            ret.push(target);
        }
    }
    return ret;
  }
   //定義一個遍歷函數(shù)
  _.each = function(target, callback) {
    console.log(target)
    var key, i = 0;
    if (_.isArray(target)) {
        var length = target.length;
        for (; i < length; i++) {
            callback.call(target, target[i], i);
        }
    } else {
        for (key in target) {
            callback.call(target, key, target[key]);
        }
    }
  }
  //開啟鏈接式的調(diào)用
  _.chain = function(obj) {
    var instance = _(obj);
    instance._chain = true;
    return instance;
  }

  //輔助函數(shù)    obj   數(shù)據(jù)結(jié)果
  var result = function(instance, obj) {
    //_(obj).chain() 會對傳入的obj創(chuàng)建一個新的實例對象,返回出去
    return instance._chain ? _(obj).chain() : obj;
  }
  //獲取_上所有綁定的函數(shù)
  _.functions = function(obj) {
    var result = [];
    var key;
    for (key in obj) {
        result.push(key);
    }
    return result;
  }
  //類型檢測
  _.isArray = function(array) {
    return toString.call(array) === "[object Array]";
  }
  //將函數(shù)加載到
  _.mixin = function(obj) {
    _.each(_.functions(obj), function(name) {
        var func = obj[name];

        _.prototype[name] = function() {
            var args = [this._wrapped];
            push.apply(args, arguments);
            return result(this, func.apply(this, args));
        }
    });
}

root._ = _
})(this)

以上基本就是underscore的源碼框架了。

  1. 先解釋_.mixin(_)
  • 函數(shù)一開始就會調(diào)用這個函數(shù),傳入的參數(shù)就是我們自定義的參數(shù),例如[1,2,3],然后調(diào)用each方法,each方法傳入兩個參數(shù),第一個參數(shù)_.functions(obj),就是獲取綁定在 _ 上面的所有函數(shù),返回的是一個數(shù)組。
  • 繼續(xù)往里走,第二個參數(shù)回去調(diào)用數(shù)組中各個函數(shù)名,拿到這個函數(shù)。
  • 繼續(xù),之后會給 _ 的prototype綁定函數(shù),
    var args = [this._wrapped]就是獲取我們傳進(jìn)來的參數(shù)[1,2,3,1]
    push.apply(args,arguments)如果后面調(diào)用方法時,還有參數(shù),則合并push進(jìn)args里。(如果不了解apply的可以看看我之前寫的call apply bind的理解這篇文章)
  • 最后調(diào)用result這個函數(shù),第一個參數(shù)this就是當(dāng)前的實例對象,第二個參數(shù)就是調(diào)用unique的方法。
  • 最后返回的結(jié)果,是調(diào)用chain后的實例對象
  1. _([1,2,3,1]).chain().unique()
  • 首先_([1,2,3,1])會生成一個實例對象,instance
  • 調(diào)用chain方法,給instance創(chuàng)建一個屬性chain并且設(shè)置為true,表示可以鏈?zhǔn)秸{(diào)用
  • 調(diào)用unique方法,此處的unique方法不是 _ 的方法,而是_.prototype的方法,也就是mixin中的函數(shù)。
  • 最后返回result的結(jié)果,也就是執(zhí)行unique方法后的實例對象。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,648評論 1 32
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,246評論 0 38
  • UnderScore對象封裝 Underscore像jQuery一樣,將數(shù)據(jù)封裝在一個自定義對象中——Unders...
    小淘氣_嘻閱讀 1,205評論 0 0
  • 幾乎在車上度過的一天。 姐姐現(xiàn)在這么成熟? 像...中年婦女,孩子上小學(xué)了的那種。
    延小狼閱讀 231評論 1 1
  • 關(guān)于中秋節(jié)的來歷,有很多古老的傳說,其中“嫦娥奔月”流傳最廣。傳說,嫦娥的丈夫后羿是一位為民除害的英雄,兩人...
    運安閣閣主閱讀 419評論 0 0

友情鏈接更多精彩內(nèi)容