zoukankan      html  css  js  c++  java
  • 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.高阶函数的使用

  • 相关阅读:
    redis
    sqlalchemy ORM
    元类的理解
    python连接mysql
    ffmpeg去水印
    ffmpeg给视频加文字水印
    yt-seo-checklist
    ffmpeg下载直播流
    ffmpeg拼接mp4视频
    ffmpeg截取视频
  • 原文地址:https://www.cnblogs.com/shytong/p/5820157.html
Copyright © 2011-2022 走看看