// 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`. /* 返回array(数组)的第一个元素。传递 n参数将返回数组中从第一个元素开始的n个元素 */ _.first = _.head = _.take = function(array, n, guard) { if (array == null || array.length < 1) return void 0; if (n == null || guard) return array[0]; 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个元素 */ _.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. /* 返回array(数组)的最后一个元素。传递 n参数将返回数组中从最后一个元素开始的n个元素 */ _.last = function(array, n, guard) { if (array == null || array.length < 1) return 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参数将返回从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. /* 返回一个除去所有false值的array副本。 在javascript中, false, null, 0, "", undefined 和 NaN 都是false值. */ _.compact = function(array) { return _.filter(array, Boolean); }; // Internal implementation of a recursive `flatten` function. /* 内部的flatten函数 */ 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]; // 如果value还是数组或类数组 // _.isArguments(value)实现原理是判断value中是否有callee方法 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 {// 否则,递归调用此函数,将结果数组output传入 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. /* 将一个嵌套多层的数组 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副本。 */ _.without = restArgs(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. // Aliased as `unique`. /* 返回 array去重后的副本, 如果您确定 array 已经排序, 那么给 isSorted 参数传递 true值, 此函数将运行的更快的算法. 如果要处理对象元素, 传递 iteratee函数来获取要对比的属性 */ _.uniq = _.unique = function(array, isSorted, iteratee, context) { // 如果没有传递或传递的isSorted不为布尔值,后面两个参数前移 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) {// 如果数组已经是排序好的,只需判断前后两个值是否相等(效率高) if (!i || seen !== computed) result.push(value); seen = computed; } else if (iteratee) {// 如果有iteratee函数,将值先push到seen中,再把value push到result中 if (!_.contains(seen, computed)) { seen.push(computed); result.push(value); } } 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. /* 返回传入的 arrays(数组)并集:按顺序返回,返回数组的元素是唯一的,可以传入一个或多个 arrays(数组) */ _.union = restArgs(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]; if (_.contains(result, item)) continue; var j; for (j = 1; j < argsLength; j++) { if (!_.contains(arguments[j], item)) break; } 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. /* 类似于without,但返回的值来自array参数数组,并且不存在于other 数组 */ _.difference = restArgs(function(array, rest) { rest = flatten(rest, true, true); return _.filter(array, function(value){ return !_.contains(rest, value);// 用indexOf() > 0来判断 }); }); // Complement of _.zip. Unzip accepts an array of arrays and groups // each array's elements on shared indices. /* 与zip功能相反的函数,给定若干arrays,返回一串联的新数组,其第一元素个包含所有的输入数组的第一元素,其第二包含了所有的第二元素,依此类推。 */ _.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);// map(array, _.property(index)) } return result; }; // Zip together multiple lists into a single array -- elements that share // an index go together. /* 将 每个arrays中相应位置的值合并在一起。在合并分开保存的数据时很有用. 如果你用来处理矩阵嵌套数组时, _.zip.apply 可以做类似的效果。 */ _.zip = restArgs(_.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. /* 将数组转换为对象。传递任何一个单独[key, value]对的列表,或者一个键的列表和一个值得列表。 如果存在重复键,最后一个值将被返回。 */ _.object = function(list, values) { var result = {}; for (var i = 0, length = getLength(list); i < length; i++) { if (values) {// 传入第二个参数 result[list[i]] = values[i]; } else {// 单独的[key,value]对 result[list[i][0]] = list[i][1]; } } return result; }; // Generator function to create the findIndex and findLastIndex functions. /* 遍历数组,找出符合predicate的项的index,找不到返回-1 */ var createPredicateIndexFinder = function(dir) { return function(array, predicate, context) { predicate = cb(predicate, context); var length = getLength(array); 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. /* 类似于_.indexOf 和_.findIndex类似,但反向迭代数组 */ _.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. /* 使用二分查找确定value在list中的位置序号,value按此序号插入能保持list原有的排序。如果提供iterator函数,iterator将作为list排序的依据,包括你传递的value 。iterator也可以是字符串的属性名用来排序(比如length)。 */ _.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') {// 如果传入了idx if (dir > 0) {// 正向 i = idx >= 0 ? idx : Math.max(idx + length, i); } else {// 反向 length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; } } else if (sortedIndex && idx && length) {// 如果没传入,但是数组有序,直接调用sortedIndex(array, item) idx = sortedIndex(array, item); return array[idx] === item ? idx : -1; } if (item !== item) {// 如果item为NaN,说明要找出是NaN的元素 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; }; }; // 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. /* 返回value在该 array 中的索引值,如果value不存在 array中就返回-1。使用原生的indexOf 函数,除非它失效。如果您正在使用一个大数组,你知道数组已经排序,传递true给isSorted将更快的用二进制搜索..,或者,传递一个数字作为第三个参数,为了在给定的索引的数组中寻找第一个匹配值。 */ _.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). /* 一个用来创建整数灵活编号的列表的函数,便于each 和 map循环。如果省略start则默认为 0;step 默认为 1.返回一个从start 到stop的整数的列表,用step来增加 (或减少)独占。值得注意的是,如果stop值在start前面(也就是stop值小于start值),那么负增长。(这个地方中文文档的解释是错的) */ _.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; }; // Split an **array** into several arrays containing **count** or less elements // of initial array. /* 把数组arr从index=0处开始,分成长度为没有重复部分的长度小于count的数组所组成的数组 */ _.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));// slice(s, e)方法当e大于数组最末尾位置时,返回从i到最后 } return result; };