underscore.js源碼解析【集合】

// Collection Functions
  // --------------------

  // The cornerstone, an `each` implementation, aka `forEach`.
  // Handles raw objects in addition to array-likes. Treats all
  // sparse array-likes as if they were dense.
  /*
    params: 數組、對象或類數組對象,函數,函數執行環境
  */
  _.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);// item index obj
      }
    } else {// 對象
      var keys = _.keys(obj);// 返回鍵的數組
      for (i = 0, length = keys.length; i < length; i++) {
        iteratee(obj[keys[i]], keys[i], obj);
      }
    }
    return obj;
  };

  // Return the results of applying the iteratee to each element.
  /*
    經過轉換函數(iteratee迭代器)映射列表中的每一個值產生價值的新數組
    這個函數很簡潔的處理類數組與對象的不一樣狀況,值得學習!!!!!!!!!!!!!!!!!!!
  */
  _.map = _.collect = function(obj, iteratee, context) {
    iteratee = cb(iteratee, context);
    var keys = !isArrayLike(obj) && _.keys(obj),//若是是數組,返回false;不然返回對象的keys數組
        length = (keys || obj).length,
        results = Array(length);
    for (var index = 0; index < length; index++) {
      var currentKey = keys ? keys[index] : index;// 若是是對象,返回key; 數組返回index
      results[index] = iteratee(obj[currentKey], currentKey, obj);
    }
    return results;
  };

  // Create a reducing function iterating left or right.
  /*
    建立迭代
    params:方向(-1爲左,1爲右)
  */
  var createReduce = function(dir) {
    // Wrap code that reassigns argument variables in a separate function than
    // the one that accesses `arguments.length` to avoid a perf hit. (#1991)
    /*
      params: obj,執行函數,起始值,起始值和context
    */
    var reducer = function(obj, iteratee, memo, initial) {
      var keys = !isArrayLike(obj) && _.keys(obj),
          length = (keys || obj).length,
          index = dir > 0 ? 0 : length - 1;
      if (!initial) {// 若是沒有起始值這個參數
        memo = obj[keys ? keys[index] : index];
        index += dir;
      }
      for (; index >= 0 && index < length; index += dir) {
        var currentKey = keys ? keys[index] : index;
        memo = iteratee(memo, obj[currentKey], currentKey, obj);
      }
      return memo;
    };

    return function(obj, iteratee, memo, context) {
      var initial = arguments.length >= 3;
      return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
    };
  };

  // **Reduce** builds up a single result from a list of values, aka `inject`,
  // or `foldl`.
  _.reduce = _.foldl = _.inject = createReduce(1);

  // The right-associative version of reduce, also known as `foldr`.
  _.reduceRight = _.foldr = createReduce(-1);

  // Return the first value which passes a truth test. Aliased as `detect`.
  /*
    在list中逐項查找,返回第一個經過predicate迭代函數真值檢測的元素值,若是沒有值傳遞給測試迭代器將返回
  */
  _.find = _.detect = function(obj, predicate, context) {
    var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey;
    var key = keyFinder(obj, predicate, context);
    if (key !== void 0 && key !== -1) return obj[key];
  };

  // Return all the elements that pass a truth test.
  // Aliased as `select`.
  /*
    遍歷list中的每一個值,返回包含全部經過predicate真值檢測的元素值
    與原生的filter函數做用相同,有原生的時候可使用原生的filter
  */
  _.filter = _.select = function(obj, predicate, context) {
    var results = [];
    predicate = cb(predicate, context);
    _.each(obj, function(value, index, list) {//遍歷全部元素,返回符合條件的value
      if (predicate(value, index, list)) results.push(value);
    });
    return results;
  };

  // Return all the elements for which a truth test fails.
  /*
    返回list中沒有經過predicate真值檢測的元素集合,與filter相反
  */
  _.reject = function(obj, predicate, context) {
    return _.filter(obj, _.negate(cb(predicate)), context);
  };

  // Determine whether all of the elements match a truth test.
  // Aliased as `all`.
  /*
    若是list中的全部元素都經過predicate的真值檢測就返回true
    與原生的every函數做用相同,有原生的時候可使用原生的every
  */
  _.every = _.all = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length;
    for (var index = 0; index < length; index++) {
      var currentKey = keys ? keys[index] : index;
      if (!predicate(obj[currentKey], currentKey, obj)) return false;//若是找到一個不符合,直接中斷函數
    }
    return true;
  };

  // Determine if at least one element in the object matches a truth test.
  // Aliased as `any`.
  /*
    若是list中有任何一個元素經過 predicate 的真值檢測就返回true。一旦找到了符合條件的元素, 就直接中斷對list的遍歷
  */
  _.some = _.any = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length;
    for (var index = 0; index < length; index++) {
      var currentKey = keys ? keys[index] : index;
      if (predicate(obj[currentKey], currentKey, obj)) return true;
    }
    return false;
  };

  // Determine if the array or object contains a given item (using `===`).
  // Aliased as `includes` and `include`.
  /*
    若是list包含指定的value則返回true
  */
  _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
    if (!isArrayLike(obj)) obj = _.values(obj);//將對象中全部value壓入一個數組
    if (typeof fromIndex != 'number' || guard) fromIndex = 0;
    return _.indexOf(obj, item, fromIndex) >= 0;
  };

  // Invoke a method (with arguments) on every item in a collection.
  /*
    在list的每一個元素上執行methodName方法
  */
  _.invoke = restArgs(function(obj, method, args) {// 將多餘三個以外的參數合併爲一個數組傳入到參數函數中
    var isFunc = _.isFunction(method);
    return _.map(obj, function(value) {
      var func = isFunc ? method : value[method];
      return func == null ? func : func.apply(value, args);
    });
  });

  // Convenience version of a common use case of `map`: fetching a property.
  _.pluck = function(obj, key) {
    return _.map(obj, _.property(key));//property() 返回獲取obj[key]的函數
  };

  // Convenience version of a common use case of `filter`: selecting only objects
  // containing specific `key:value` pairs.
  /*
    遍歷list中的每個值,返回一個數組,這個數組包含properties所列出的屬性的全部的 鍵 - 值對
  */
  _.where = function(obj, attrs) {
    return _.filter(obj, _.matcher(attrs));
  };

  /*
    遍歷整個list,返回匹配 properties參數所列出的全部 鍵 - 值 對的第一個值。
  */
  // Convenience version of a common use case of `find`: getting the first object
  // containing specific `key:value` pairs.
  _.findWhere = function(obj, attrs) {
    return _.find(obj, _.matcher(attrs));
  };

  // Return the maximum element (or element-based computation).
  /*
    返回list中的最大值。若是傳遞iteratee參數,iteratee將做爲list中每一個值的排序依據。若是list爲空,將返回-Infinity
  */
  _.max = function(obj, iteratee, context) {
    var result = -Infinity, lastComputed = -Infinity,
        value, computed;
    if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) {
      // 不存在iteratee參數,或者iteratee爲數字類型m,obj[0]是object類型??????????????????????????????????????????
      obj = isArrayLike(obj) ? obj : _.values(obj);// 判斷obj是否是數組或類數組(有沒有正確類型的length)
      for (var i = 0, length = obj.length; i < length; i++) {
        value = obj[i];
        if (value != null && value > result) {
          result = value;
        }
      }
    } else {
      // 存在iteratee參數
      iteratee = cb(iteratee, context);
      // 利用each對整個數組進行操做
      _.each(obj, function(v, index, list) {
        computed = iteratee(v, index, list);
        if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
          result = v;
          lastComputed = computed;
        }
      });
    }
    return result;
  };

  // Return the minimum element (or element-based computation).
  /*
    返回list中的最小值。若是傳遞iteratee參數,iteratee將做爲list中每一個值的排序依據。若是list爲空,將返回-Infinity
    實現與上述一致
  */
  _.min = function(obj, iteratee, context) {
    var result = Infinity, lastComputed = Infinity,
        value, computed;
    if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) {
      obj = isArrayLike(obj) ? obj : _.values(obj);
      for (var i = 0, length = obj.length; i < length; i++) {
        value = obj[i];
        if (value != null && value < result) {
          result = value;
        }
      }
    } else {
      iteratee = cb(iteratee, context);
      _.each(obj, function(v, index, list) {
        computed = iteratee(v, index, list);
        if (computed < lastComputed || computed === Infinity && result === Infinity) {
          result = v;
          lastComputed = computed;
        }
      });
    }
    return result;
  };

  // Shuffle a collection.
  /*
    返回一個隨機亂序的 list 副本, 使用 Fisher-Yates shuffle 來進行隨機亂序
  */
  _.shuffle = function(obj) {
    return _.sample(obj, Infinity);
  };

  // Sample **n** random values from a collection using the modern version of the
  // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
  // If **n** is not specified, returns a single random element.
  // The internal `guard` argument allows it to work with `map`.
  /*
    從 list中產生一個隨機樣本。傳遞一個數字表示從list中返回n個隨機元素。不然將返回一個單一的隨機項
  */
  _.sample = function(obj, n, guard) {
    if (n == null || guard) {// 沒傳遞n或者是傳遞了guard參數
      if (!isArrayLike(obj)) obj = _.values(obj);
      return obj[_.random(obj.length - 1)];// _.random()利用Math.random返回隨機數組下標的值
    }
    var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
    var length = getLength(sample);
    n = Math.max(Math.min(n, length), 0);// 取n和length的最小值
    var last = length - 1;
    // 選出數量爲n的隨機數
    // 這裏不用arr.sort()方法是由於利用數組排序進行隨機排列會有分佈不均的現象,具體見https://www.h5jun.com/post/array-shuffle.html
    for (var index = 0; index < n; index++) {
      var rand = _.random(index, last);
      var temp = sample[index];
      sample[index] = sample[rand];
      sample[rand] = temp;
    }
    return sample.slice(0, n);
  };

  // Sort the object's values by a criterion produced by an iteratee.
  /*
    返回一個排序後的list拷貝副本。若是傳遞iteratee參數,iteratee將做爲list中每一個值的排序依據。
  */
  _.sortBy = function(obj, iteratee, context) {
    var index = 0;
    iteratee = cb(iteratee, context);
    // _.pluck()返回對象數組中全部對象的value屬性組成的數組
    return _.pluck(_.map(obj, function(value, key, list) {// 把數組中每一個值包裝成一個對象,返回一個對象數組
      return {
        value: value,
        index: index++,
        criteria: iteratee(value, key, list)
      };
    }).sort(function(left, right) {// 數組中的值按照函數執行結果或字符串進行排序
      var a = left.criteria;
      var b = right.criteria;
      if (a !== b) {
        if (a > b || a === void 0) return 1;
        if (a < b || b === void 0) return -1;
      }
      return left.index - right.index;
    }), 'value');
  };

  // An internal function used for aggregate "group by" operations.
  /*
    params: 執行函數,是否分紅兩份????????????????????????????
  */
  var group = function(behavior, partition) {
    // 返回一個接受三個參數的函數
    // 對傳入數組每一項執行iteratee並將結果傳入behavior執行
    return function(obj, iteratee, context) {
      var result = partition ? [[], []] : {};
      iteratee = cb(iteratee, context);
      _.each(obj, function(value, index) {
        var new_val = iteratee(value, index, obj);// 這裏參數名稱改成new_val感受更好理解一點
        behavior(result, value, new_val);
      });
      return result;
    };
  };

  // Groups the object's values by a criterion. Pass either a string attribute
  // to group by, or a function that returns the criterion.
  /*
    把一個集合分組爲多個集合,經過 iterator 返回的結果進行分組. 若是 iterator 是一個字符串而不是函數, 那麼將使用 iterator 做爲各元素的屬性名來對比進行分組
    params: [[], []]或{},每一項的value與對應iteratee(value, index, obj)的結果
  */
  _.groupBy = group(function(result, value, key) {
    if (_.has(result, key)) result[key].push(value); else result[key] = [value];// 若是result中已經有了key屬性,則push(value);不然新增屬性key
  });

  // Indexes the object's values by a criterion, similar to `groupBy`, but for
  // when you know that your index values will be unique.
  /*
    給定一個list,和 一個用來返回一個在列表中的每一個元素鍵 的iterator 函數(或屬性名), 返回一個每一項索引的對象。
  */
  _.indexBy = group(function(result, value, key) {
    result[key] = value;
  });

  // Counts instances of an object that group by a certain criterion. Pass
  // either a string attribute to count by, or a function that returns the
  // criterion.
  /*
    排序一個列表組成一個組,而且返回各組中的對象的數量的計數。
    相似groupBy,可是不是返回列表的值,而是返回在該組中值的數目。
  */
  _.countBy = group(function(result, value, key) {
    if (_.has(result, key)) result[key]++; else result[key] = 1;
  });

  /*
    第一個表示不包含代理對代碼點的全部字符
    第二個表示合法的代理對的全部字符
    第三個表示代理對的代碼點(自己不是合法的Unicode字符)
    意思就是全部字符,'hello'.match(reStrSymbol); ==> ['h','e','l','l','o']
  */
  var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
  // Safely create a real, live array from anything iterable.
  /*
    把obj(任何能夠迭代的對象)轉換成一個數組,在轉換 arguments 對象時很是有用
  */
  _.toArray = function(obj) {
    if (!obj) return [];
    if (_.isArray(obj)) return slice.call(obj);// 若是是純數組,直接用slice.call()
    if (_.isString(obj)) {//若是是字符串
      // Keep surrogate pair characters together
      return obj.match(reStrSymbol);
    }
    if (isArrayLike(obj)) return _.map(obj, _.identity);// 若是是對象相似於_.kets(obj)
    return _.values(obj);
  };

  // Return the number of elements in an object.
  /*
    返回list的長度。
  */
  _.size = function(obj) {
    if (obj == null) return 0;
    return isArrayLike(obj) ? obj.length : _.keys(obj).length;
  };

  // Split a collection into two arrays: one whose elements all satisfy the given
  // predicate, and one whose elements all do not satisfy the predicate.
  /*
    拆分一個數組(array)爲兩個數組:  第一個數組其元素都知足predicate迭代函數, 而第二個的全部元素均不能知足predicate迭代函數。
    pass爲傳入迭代函數的返回值
  */
  _.partition = group(function(result, value, pass) {
    result[pass ? 0 : 1].push(value);
  }, true);

 

小結

1.處理類數組與對象的不一樣狀況

var keys = !isArrayLike(obj) && _.keys(obj),//若是是數組,返回false;不然返回對象的keys數組
        length = (keys || obj).length,
        results = Array(length);

2.高階函數的使用

相關文章
相關標籤/搜索