// Array Functions// ---------------// 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`.
_.first = _.head = _.take = function(array, n, guard) {
//如果array为空的情况下,n也为空返回undefined,n不为空返回[]if (array == null || array.length < 1) return n == null ? void 0 : [];//n为空,返回数组第一个元素if (n == null || guard) return array[0];//返回包含数组前n个元素的数组return _.initial(array, array.length - n);
};
// 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.// 返回数组中除了最后一个元素外的其他全部元素。 在arguments对象上特别有用。// 传递 n参数将从结果中排除从最后一个开始的n个元素(注:排除数组后面的 n 个元素)。
_.initial = function(array, n, guard) {
return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
};
// Get the last element of an array. Passing **n** will return the last N// values in the array.// 返回数组的后n个元素
_.last = function(array, n, guard) {
if (array == null || array.length < 1) return n == null ? void 0 : [];if (n == null || guard) return array[array.length - 1];return _.rest(array, Math.max(0, array.length - n));
};
// 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.// 返回从n个索引开始的元素
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, n == null || guard ? 1 : n);
};
// Trim out all falsy values from an array.// 返回数组中的真值 _.compact = function(array) { return _.filter(array, Boolean); };// Internal implementation of a recursive `flatten` function.// 将嵌套多层数组转换为一位数组 var flatten = function(input, shallow, strict, output) { output = output || []; var idx = output.length; for (var i = 0, length = getLength(input); i < length; i++) { var value = input[i]; // 元素类型为数组或者参数 if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { // Flatten current level of array or arguments object. // 传入shallow的情况下,只展开一层就不再深入 if (shallow) { var j = 0, len = value.length; while (j < len) output[idx++] = value[j++]; } else {// 否则,迭代展开。 flatten(value, shallow, strict, output); idx = output.length; } } else if (!strict) {//非严格模式下,将元素加入输出中。 output[idx++] = value; } } return output; };// Flatten out an array, either recursively (by default), or just one level.// 展开嵌套的多层数组
_.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副本。(注:使用===表达式做相等测试。)
_.without = restArguments(function(array, otherArrays) {
return _.difference(array, otherArrays);
});
// Produce a duplicate-free version of the array. If the array has already// been sorted, you have the option of using a faster algorithm.// The faster algorithm will not work with an iteratee if the iteratee// is not a one-to-one function, so providing an iteratee will disable// the faster algorithm.// Aliased as `unique`.// 返回 array去重后的副本, 使用 === 做相等测试. // 如果您确定 array 已经排序, 那么给 isSorted 参数传递 true值, 此函数将运行的更快的算法. // 如果要处理对象元素, 传递 iteratee函数来获取要对比的属性。
_.uniq = _.unique = function(array, isSorted, iteratee, context) {
// 类似jq,做参数兼容方案if (!_.isBoolean(isSorted)) { context = iteratee; iteratee = isSorted; isSorted = false;}if (iteratee != null) iteratee = cb(iteratee, context);var result = [];var seen = [];for (var i = 0, length = getLength(array); i < length; i++) { var value = array[i], computed = iteratee ? iteratee(value, i, array) : value; // 如果数组有序,且不存在迭代器 if (isSorted && !iteratee) { // 如果计算结果不等于seen中,或者i为第一个元素,将元素存入result中 if (!i || seen !== computed) result.push(value); // 将seen替换为最新结果 seen = computed; } else if (iteratee) {//如果存在迭代器 if (!_.contains(seen, computed)) {//如果seen中不包含计算结果 seen.push(computed);//计算结果存入seen中 result.push(value);//元素存入result中 } } else if (!_.contains(result, value)) {//如果不存在迭代器 result.push(value); }}return result;
};
// Produce an array that contains the union: each distinct element from all of// the passed-in arrays. _.union = restArguments(function(arrays) { // 将数组展开后去重 return _.uniq(flatten(arrays, true, true)); });// Produce an array that contains every item shared between all the// passed-in arrays.// 返回传入 arrays(数组)交集。结果中的每个值是存在于传入的每个arrays(数组)里。
_.intersection = function(array) {
var result = [];var argsLength = arguments.length;for (var i = 0, length = getLength(array); i < length; i++) { var item = array[i]; // 元素已存在于result中,则继续下一个循环 if (_.contains(result, item)) continue; var j; for (j = 1; j < argsLength; j++) { if (!_.contains(arguments[j], item)) break; } //如果所有参数都包含item,则item为交集元素 if (j === argsLength) result.push(item);}return result;
};
// Take the difference between one array and a number of other arrays.// Only the elements present in just the first array will remain.// array中与rest不同的值 _.difference = restArguments(function(array, rest) { rest = flatten(rest, true, true); return _.filter(array, function(value) { return !_.contains(rest, value); }); });// Complement of _.zip. Unzip accepts an array of arrays and groups// each array's elements on shared indices.//
_.unzip = function(array) {
var length = array && _.max(array, getLength).length || 0;var result = Array(length);for (var index = 0; index < length; index++) { result[index] = _.pluck(array, index);}return result;
};
// Zip together multiple lists into a single array -- elements that share// an index go together.
_.zip = restArguments(_.unzip);
// 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. Passing by pairs is the reverse of _.pairs.
_.object = function(list, values) {
var result = {};for (var i = 0, length = getLength(list); i < length; i++) { if (values) { result[list[i]] = values[i]; } else { result[list[i][0]] = list[i][1]; }}return result;
};
// Generator function to create the findIndex and findLastIndex functions.
var createPredicateIndexFinder = function(dir) {
return function(array, predicate, context) { predicate = cb(predicate, context); var length = getLength(array); //正向起始位置为0,反向起始位置为数组末尾length-1 var index = dir > 0 ? 0 : length - 1; for (; index >= 0 && index < length; index += dir) { if (predicate(array[index], index, array)) return index; } return -1;};
};
// Returns the first index on an array-like that passes a predicate test.
_.findIndex = createPredicateIndexFinder(1);
_.findLastIndex = createPredicateIndexFinder(-1);
// Use a comparator function to figure out the smallest index at which// an object should be inserted so as to maintain order. Uses binary search.// 假设array是有序,且是升序// 返回iteratee(obj)值在array中的iteratee(array(index))的位置index
_.sortedIndex = function(array, obj, iteratee, context) {
iteratee = cb(iteratee, context, 1);var value = iteratee(obj);var low = 0, high = getLength(array);while (low < high) { var mid = Math.floor((low + high) / 2); if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;}return low;
};
// Generator function to create the indexOf and lastIndexOf functions.
var createIndexFinder = function(dir, predicateFind, sortedIndex) {
return function(array, item, idx) { var i = 0, length = getLength(array); //存在初始位置 if (typeof idx == 'number') { //正向,改变起始位置 if (dir > 0) { //起始位置 = idx为正?idx:取(length+idx)与0中的最大值 i = idx >= 0 ? idx : Math.max(idx + length, i); } else {//反向,改变数组长度 //lastIndexOf函数功能: //lastIndexOf(searchValue,fromIndex) //若在0-fromIndex之间存在searchValue,则返回最后一个出现的位置 //fromIndex为正值,范围则是0-abs(fromIndex) //fromIndex为负值,范围则是0-(length-abs(fromIndex))??此处为什么有+1 //看了原生lastIndexOf中就是如此设置的。 //数组长度=idx为正?取(idx+1,length):idx + length + 1 length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; } //idx数值以外的值(isSorted=true),默认array为有序数组,使用sortedIndex查找item位置 } else if (sortedIndex && idx && length) { idx = sortedIndex(array, item); return array[idx] === item ? idx : -1; } //--------------自我理解可简写---------- //查找值为NaN if (item !== item) { idx = predicateFind(slice.call(array, i, length), _.isNaN); return idx >= 0 ? idx + i : -1; } for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { if (array[idx] === item) return idx; } return -1; //--------------自我理解可简写为如下---------- var iteratee = null; if(item!==item) iteratee = _.isNaN idx = predicateFind(slice.call(array, i, length), iteratee); return idx >= 0 ? idx + i : -1; };
};
// Return the position of the first occurrence of an item in an array,// or -1 if the item is not included in the array.// If the array is large and already in sort order, pass `true`// for **isSorted** to use binary search.// 如果array数值大且是升序,可以设置_.indexOf(array,item,isSorted=true),使用二进制搜索。
_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
_.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
// Generate an integer Array containing an arithmetic progression. A port of// the native Python `range()` function. See// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) {
if (stop == null) { stop = start || 0; start = 0;}if (!step) { step = stop < start ? -1 : 1;}var length = Math.max(Math.ceil((stop - start) / step), 0);var range = Array(length);for (var idx = 0; idx < length; idx++, start += step) { range[idx] = start;}return range;
};
// Chunk a single array into multiple arrays, each containing `count` or fewer// items.
_.chunk = function(array, count) {
if (count == null || count < 1) return [];var result = [];var i = 0, length = array.length;while (i < length) { result.push(slice.call(array, i, i += count));}return result;
};