我看underscore 源碼設計

先過濾掉underscore內部各個工具函數的具體邏輯,只看源碼庫自己有什麼內容。

構造函數

underscore有兩種調用方式:javascript

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

_是一個函數對象,api中的函數全都掛載到_上,實現_.funcjava

// 使用當即執行函數
(function () {
      // 定義全局對象
      var root = typeof self == 'object' && self.self === self && self ||
            typeof global == 'object' && global.global === global && global ||
            this ||
            {};
     // 省略...
     // 建立_對象
     var _ = function(obj) {
        // 若是參數是underscore的一個實例,就直接返回該參數  
        if (obj instanceof _) return obj;
        //  對應_(xxx)調用,若是new實例不是_的實例,就返回實例化_對象
        if (!(this instanceof _)) return new _(obj);
        // 並將數據對象包裝在實例對象上 
           this._wrapped = obj;
       };
     // 省略...
     
     //註冊在全局對象
     root._=_; 
    
})();

mixin

上一步中,咱們建立了underscore實例,只能支持_.func調用,若是要支持_(obj).func,同時還要將func註冊在實例的prototype上。試想下,若是每聲明一個函數,就要綁定一次,那得用多…編程

在underscore中,使用mixin將自定義函數添加到Underscore對象。api

// 用於返回排序後的數組,包含全部的obj中的函數名。
_.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的函數,綁定在原型上
    _.each(_.functions(obj), function(name) {  
      var func = _[name] = obj[name];
      _.prototype[name] = function() {
        // this._wrapped做爲第一個參數傳遞,其餘用戶傳遞的參數放在後面。  
        var args = [this._wrapped];
        push.apply(args, arguments);
        return chainResult(this, func.apply(_, args));
      };
    });
    return _;
};
// 執行混入
 _.mixin(_);

大體流程是:數組

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

_自身定義一系列函數,經過_.mixin()綁定在了_.prototype上,提升了代碼的複用度。app

鏈式調用

相似於 Java Stream 流式編程函數

1584758293868.png

在Javascript中,數據能夠像是在管道中流通,咱們稱之爲,聲明式編程/連接式調用工具

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

既然要知足連接式調用(Chaining)語法,須要知足兩個條件學習

  1. 前一次調用返回數據對象,用來給下一個函數調用提供數據來源
  2. 返回調用當前函數的對象,以保證能夠調用下個函數

可能會想到,在每一個函數結尾return this;,對,能用可是不必,除了要手動添加外,咱們也沒法關閉鏈式。this

underscore中,使用的是可選式實現連接編程,使用.chain()來開啓鏈式調用,而後使用.value()獲取函數的最終值。

關鍵函數:

// 開啓鏈式調用
_.chain = function (obj) { 
    //返回一個封裝的對象. 在封裝的對象上調用方法會返回封裝的對象自己
    var instance = _(obj)
    instance._chain = true //檢測對象是否支持鏈式調用
    return instance
}
// 輔助函數:鏈式中轉
// 鏈式調用 將數據對象封裝在underscore實例中
// 非鏈式調用 返回數據對象
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,而是經過將其設計思想轉換爲本身的。經過以上幾點,咱們能夠大概實現一個簡化版的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
  }
  // 獲取對象上的函數
  _.functions = function (obj) {
    var result = []
    for (let key in obj) {
      result.push(key)
    }
    return result
  }
  // 執行鏈式操做
  _.chain = function (obj) { //數據源
    var instance = _(obj)
    instance._chain = true //檢測對象是否支持鏈式調用
    return instance
  }
  //輔助函數 將數據包裝爲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
  }
  // 關閉鏈式調用 返回數據自己
  _.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)
  }
  // 檢測靜態方法 name 存放在數組中
  // 遍歷數組 給_.prototype進行註冊
  _.mixin = function (obj) {
    _.each(_.functions(obj), function (key) {
      var func = obj[key]
      //console.log(key)
      _.prototype[key] = function () {
        //console.log(this.warp) //數據源
        //console.log(arguments) //callback
        // 進行參數合併
        var args = [this.warp]
        Array.prototype.push.apply(args, arguments)
        return ret(this, func.apply(this, args))
      }
    })
  }
  _.mixin(_)
  root._ = _
})(this)

調用:

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())
相關文章
相關標籤/搜索