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);
};
相关文章
相关标签/搜索