underscore源碼學習筆記(一)

underscore源碼學習筆記

1、集合

  1. 首先是幾個迭代的方法。
_.each = _.forEach = function(obj, iteratee, context) {
    iteratee = optimizeCb(iteratee, context);
    var i, length;
    if (isArrayLike(obj)) {
      for (i = 0, length = obj.length; i < length; i++) {
        iteratee(obj[i], i, obj);
      }
    } else {
      var keys = _.keys(obj);
      for (i = 0, length = keys.length; i < length; i++) {
        iteratee(obj[keys[i]], keys[i], obj);
      }
    }
    // 鏈式調用
    return obj;
};

ES爲數組一樣添加了原生的forEach()方法。不一樣的是這裏的each(forEach)方法能夠對全部集合使用,函數接受三個參數(集合、迭代函數、執行環境)。javascript

optimizeCb函數根據迭代函數參數個數的不一樣爲不一樣的迭代方法綁定了相應的執行環境,forEach迭代函數一樣接受三個參數(值,索引,集合)。
接下來就是for循環調用迭代函數了。java

_.map中一種更優雅的判斷isArrayLike的實現方式:(只用一個for循環)數組

var keys = !isArrayLike(obj) && _.keys(obj),
    length = (keys || obj).length,
    results = Array(length);
    for (var index = 0; index < length; index++) {
      var currentKey = keys ? keys[index] : index;
      results[index] = iteratee(obj[currentKey], currentKey, obj);
    }
return results;
    // 合理使用&&、||、?:能夠大大減小代碼量

還有兩個特別的地方:ide

  • 將集合分紅了類數組集合和對象集合。使用了isArrayLike函數:
// js的最大精確整數
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var isArrayLike = function(collection) {
    var length = collection != null && collection.length;
    return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
}; // 若是集合有Length屬性且爲數字而且大於0小於最大的精確整數,則斷定是類數組
  • 使用了_.keys函數,Object一樣有原生的keys函數,用於返回一個集合obj可被枚舉的屬性數組。實現比較簡單,for in加上hasOwnProperty()方法。

_.map,_.reduce方法原理相似.
_.find函數和Array.some()相似,不一樣的是返回的是第一個使迭代結果爲真的那個元素,而不是Array.some()那樣返回布爾值。函數

_.find = _.detect = function(obj, predicate, context) {
    var key;
    if (isArrayLike(obj)) {
      key = _.findIndex(obj, predicate, context);
    } else {
      key = _.findKey(obj, predicate, context);
    }
    if (key !== void 0 && key !== -1) return obj[key];
};
function createIndexFinder(dir) {
    return function(array, predicate, context) {
      predicate = cb(predicate, context);
      var length = array != null && array.length;
      // 若是dir爲1,index爲0,index+=1,index正序循環
      // 若是dir 爲-1,index爲length-1,index += -1反序循環
      // 判斷循環條件則用了index >= 0 && index < length方法兼顧兩種循環方式
      var index = dir > 0 ? 0 : length - 1;
      
      for (; index >= 0 && index < length; index += dir) {
        if (predicate(array[index], index, array)) return index;
      }
      return -1;
    };
}
_.findIndex = createIndexFinder(1);
_.findLastIndex = createIndexFinder(-1);

值得借鑑的地方是這裏的一個for循環可以根據傳入的參數不一樣配置不一樣的循環順序。學習

  1. 集合中的其餘方法基本都是基於迭代方法來實現的。
_.max = function(obj, iteratee, context) {
    var result = -Infinity, lastComputed = -Infinity,
        value, computed;
    if (iteratee == null && obj != null) {
      obj = isArrayLike(obj) ? obj : _.values(obj);
      for (var i = 0, length = obj.length; i < length; i++) {
        value = obj[i];
        if (value > result) {
          result = value;
        }
      }
    } else {
      iteratee = cb(iteratee, context);
      _.each(obj, function(value, index, list) {
        computed = iteratee(value, index, list);
        if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
          result = value;
          lastComputed = computed;
        }
      });
    }
   return result;
};

max方法用於尋找集合中的最大值,經過循環list中的全部項,而後比較當前項和結果項,若是當前項大於結果,則將其賦給結果項,最後返回結果項。this

  1. 集合轉換爲數組
_.toArray = function(obj) {
     if (!obj) return [];
     // 若是是數組,採用了Array.prototype.slice.call(this,obj)這種方法
     if (_.isArray(obj)) return slice.call(obj);
     // 類數組對象,這裏沒有采用Slice方法,而是利用.map對集合進行迭代,從而返回一個數組。 _.identity該方法傳入的值和返回的值相等。(主要用於迭代)
     xif (isArrayLike(obj)) return _.map(obj, _.identity);
     // 普通對象,則返回由屬性值組成的數組。
     return _.values(obj);
};
相關文章
相關標籤/搜索