V-1.8.2 underscore源码解析(五)
_.countBy = group(function(result, value, key) {
if(_.has(result, key)) result[key]++;
else result[key] = ;
});
// _.countBy([1, 2, 3, 4, 5], function(num) {
// return num % 2 == 0 ? 'even': 'odd';
// });
// => {odd: 3, even: 2}
// Safely create a real, live array from anything iterable.
// 把list(任何可以迭代的对象)转换成一个数组,在转换 arguments 对象时非常有用。
_.toArray = function(obj) {
// 将boolean、为falsy值得字符串、null 、undefined
// 返回一个空数组
// 伪数组 -> 数组
// 对象 -> 提取 value 值组成数组
if(!obj) return [];
if(_.isArray(obj)) return slice.call(obj);
// 如果是类数组,则重新构造新的数组
if(isArrayLike(obj)) return _.map(obj, _.identity);
// 如果是对象,则返回 values 集合
return _.value(obj);
}
// _.toArray({name:"tom",age:23})
// (2) ["tom", 23]
// _.toArray('121e1wwef')
// (9) ["1", "2", "1", "e", "1", "w", "w", "e", "f"]
// _.toArray(true)
// []
// _.toArray(' ')
// [" "]
// Return the number of elements in an object.
// 返回obj中元素的个数
_.size = function(obj) {
if(obj == null) return ;
// 若是类数组,则返回数组的长度,若是对象,则返回key构成的数组的长度
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.
// 将一个集合划分成2个数组,一个全部满足predicate函数真值检测,另一个全不满足
_.partition = function(obj, predicate, context) {
predicate = cb(predicate, context);
var pass = [],
fail = [];
_.each(obj, function(value, key, obj) {
(predicate(value, key, obj) ? pass : fail).push(value);
});
// 返回一个二维数组,元素为[pass]和[fail]数组
return [pass, fail];
};
// Array Functions 数组的扩展方法
// ---------------
// 所有的数组函数也可以用于 arguments (参数)对象。 但是,Underscore 函数不能用于稀疏("sparse" )数组。
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
// 获取数组中的第一个元素,通过设定参数n值,返回数组前 n 个元素(组成的数组)
_.first = _.head = _.take = function(array, n, guard) {
// array为null或者undefined ,则返回undefined
// array只能为数组或者在arguments对象
if(array == null) return void ;
// 如果n 和 guard都存在且不为null/undefined,或都不存在,则返回数组的第一个值
if(n == null || guard) return array[];
// 只存在n,返回数组前n个元素构成的数组
return _.initial(array, array.length - n);
};
// _.first([2,3,5,6,7],2)
// (2) [2, 3]
// _.first([2,3,5,6,7],2,4)
// 2
// _.first([2,3,5,6,7])
// 2
// _.first({name:'tom',age:23,sex:'men'},0)
// []
// _.first({name:'tom',age:23,sex:'men'})
// undefined
/*
<div>2</div>
<div>3</div>
<div>4</div>
var div = document.getElementsByTagName('div');
var res1 = _.first(div,2);
[div, div]
var res = _.first(div,1)[0].innerHTML;
console.log(res); //2
*/
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N.
// 与_.first相反,
// 传入一个数组
// 返回剔除最后一个元素之后的数组副本
// 如果传入参数 n,则剔除最后 n 个元素
_.initial = function(array, n, guard) {
return slice.call(array, , Math.max(, array.length - (n == null || guard ? : n)));
};
// _.initial([2,4,6,8],2)
// (2) [2, 4]
// _.initial([2,4,6,8],1)
// (3) [2, 4, 6]
// Get the last element of an array. Passing **n** will return the last N
// values in the array.
// last和first方法相反,last是从后面开始计数,
// 返回array(数组)中最后一个元素。传递 n参数将返回
// 数组中从最后一个元素开始的n个元素(返回数组里的后面
// 的n个元素)。
_.last = function(array, n, guard) {
if(array == null) return void ;
if(n == null || guard) return array[array.length - ];
return _.rest(array, Math.max(, array.length - n));
};
// _.last([3,5,7,8],2)
// (2) [7, 8]
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array.
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, n == null || guard ? : n);
};
// rest_.rest(array, [index]) Alias: tail, drop
// 返回数组中除了第一个元素外的其他全部元素。
// 传递 index 参数将返回从index开始的剩余所有元素 。
// Trim out all falsy values from an array.
// 返回一个去除了false值的array副本,在JavaScript中属于falsy值得
// 有 false ,0,'',null,undefined,NaN
_.compact = function(array) {
return _.filter(array, _.identity);
}
// 递归的一个内部实现
// Internal implementation of a recursive `flatten` function
var flatten = function(input, shallow, strict, startIndex) {
var output = [],
idx = ;
for(var i = startIndex || , length = input && input.length; i < length; i++) {
var value = input[i];
// 判断input中参数是否为数组
if(isArrayLike() && (_.Array(value) || _.isArguments(value))) {
// 根据shallow的值来决定是否继续递归
if(!shallow) value = flatten(value, shallow, strict);
// 将value中的值添加到output新数组中
var j = ,
len = value.length;
while(j < len) {
output[idx++] = value[j++];
}
// 若不是数组,则直接将value放入新数组
} else if(!strict) {
output[idx++] = value;
}
}
//返回的是一个新数组
return output;
}
// Flatten out an array, either recursively (by default), or just one level.
// 将一个嵌套多层的数组 array(数组) (嵌套可以是任何层数)转换为只有一层的数组。 如果你传递 shallow参数,数组将只减少一维的嵌套。
_.flatten = function(array, shallow) {
return flatten(array, shallow, false);
};
// Return a version of the array that does not contain the specified value(s).
//返回一个删除所有values值后的 array副本。(愚人码头注:使用===表达式做相等测试。),就相当于一个过滤函数,将array中包含values的值全部去除掉,返回一个剩余值得新数组
_.without = function(array, * values) {
return _.difference(array, slice.call(arguments, ));
};
// 返回 array去重后的副本, 使用 === 做相等测试. 如果您确定 array 已经排序, 那么给 isSorted 参数传递 true值, 此函数将运行的更快的算法. 如果要处理对象元素, 传递 iteratee函数来获取要对比的属性.
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iteratee, context) {
if(array == null) return [];
// 判断是否已进行排序,若已排序,节省运算步骤,能提高速度
if(!_.isBoolean(isSorted)) {
context = iteratee;
iteratee = isSorted;
isSorted = false;
}
if(iteratee != null) iteratee = cb(iteratee, context);
var result = [];
var seen = [];
for(var i = , length = array.length; i < length; i++) {
var value = array[i],
computed = iteratee ? iteratee(value, i, array) : value;
if(isSorted) {
if(!i || seen !== computed) result.push(value);
seen = computed;
} else if(iteratee) {
// 判断是否存在,不存在则push
if(!_.contains(seen, computed)) {
seen.push(computed);
result.push(value);
}
} else if(!_.contains(result, value)) {
result.push(value);
}
}
return result;
};
// _.union(*arrays)
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
// 返回传入的 arrays(数组)并集:按顺序返回,返回数组的元素是唯一的,可以传入一个或多个 arrays (数组),由于其中调用了flatten方法,而flatten方法可以将嵌套数组转为一维数组,由于第二个参数是true,故flatten是将嵌套数组减一维进行运算,故可以传入多个数组,然后在调用uniq方法进行去重【' === '】
_.union = function() {
return _.uniq(flatten(arguments, true, true));
};
// 返回一个交叉数组,即每个数组中都含有的项,即多个数组的交集
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
// 若array为null或者undefined,则返回一个空数组
if(array == null) return [];
var result = [];
var argsLength = arguments.length;
// 依次将array中的每一项中的item和arguments的下一项比较,当比较到最后一项时,若这些项中都含有item,则将其push如result数组
for(var i = , length = array.length; i < length; i++) {
var item = array[i];
if(_.contains(result, item)) continue;
for(var j = ; j < argsLength; j++) {
if(!_.contains(arguments[j], item)) break;
}
if(j === argsLength) result.push(item);
}
return result;
};
// 将第一个数组和另一个数组比较,类似于without,返回的是第一个数组中的值组成的副本,若第一个数组和另一个数组含有相同的值,则返回的新数组需要去除这些相同的值
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
var rest = flatten(arguments, true, true, );
return _.filter(array, function(value) {
return !_.contains(rest, value);
});
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
// 将 每个arrays中相应位置的值合并在一起。在合并分开保存的数据时很有用. 如果你用来处理矩阵嵌套数组时, _.zip.apply 可以做类似的效果。
_.zip = function() {
return _.unzip(arguments);
};
// _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
//=> [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]
// Complement of _.zip. Unzip accepts an array of arrays and groups
// each array's elements on shared indices
// 与zip功能相反的函数,给定若干arrays,返回一串联的新数组,其第一元素个包含所有的输入数组的第一元素,其第二包含了所有的第二元素,依此类推。通过apply用于传递数组的数组。
_.unzip = function(array) {
var length = array && _.max(array, 'length').length || ;
// 声明一个指定长度的数组
var result = Array(length);
// 将多个数组中的相同索引的值放入一个数组
for(var index = ; index < length; index++) {
result[index] = _.pluck(array, index);
}
return result;
};
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
// 将数组转换为对象。传递任何一个单独[key, value]对的列表,或者一个键的列表和一个值得列表。 如果存在重复键,最后一个值将被返回。
_.object = function(list, values) {
var result = {};
for(var i = , length = list && list.length; i < length; i++) {
if(values) {
result[list[i]] = values[i];
} else {
result[list[i][]] = list[i][];
}
}
return result;
};