乐趣区

underscore.js 源码学习 array(未完待续)

// 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;

};

退出移动版