Underscore源码(3)

2016-12-09  本文已影响16人  ____雨歇微凉
// Array Functions
// ---------------




/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * first
 * 返回第一个元素 或者从第一个元素开始到第n个元素
 */
// 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 不存在 则返回undefined
    if (array == null) return void 0;
    // 如果n不存在,则返回第一个
    if (n == null || guard) return array[0];
    // 否则返回第1个到n  使用了_.initial(), 把后面不要的扔掉正好是需要的
    return _.initial(array, array.length - n);
};

/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * initial
 * 返回数组中除了最后一个元素外的其他全部元素。如果有n ,就把后面n个就干掉了
 * 意思就是干掉后面多少个,类似pop,只不过可以干掉多个
 */
// 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.
_.initial = function(array, n, guard) {
    // 拆分array, 从第0个拆分到指定位置 ,n 把后面n个就干掉了
    return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
};

/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * last
 * 和first相反,返回最后一个,或者后面n个
 */
// Get the last element of an array. Passing **n** will return the last N
// values in the array.
_.last = function(array, n, guard) {
    if (array == null) return void 0;
    if (n == null || guard) return array[array.length - 1];
    //  _.rest() 没有参数n则返回最后一个,否则返回length-n 到最后一个
    return _.rest(array, Math.max(0, array.length - n));
};
/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * rest
 * 从已有数组中返回指定字符串,原生方法
 */
// 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 ? 1 : n);
};
/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * compact
 * 返回一个除去所有false值的 array副本。
 */
// Trim out all falsy values from an array.
// 因为_.identity返回的就是本身,而filter返回的是所有真值,所以会返回一个除去所有false值的 array副本。
// 在javascript中, false, null, 0, "", undefined 和 NaN 都是false值.
_.compact = function(array) {
    return _.filter(array, _.identity);
};
/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * flatten
 * 将嵌套数组扁平化,如果shallow为true,则只铺平一层
 */
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, strict, startIndex) {
    var output = [], idx = 0;
    for (var i = startIndex || 0, length = input && input.length; i < length; i++) {
        var value = input[i];

        // 处理如果input[i],仍然是数组的情况 , else ,直接添加
        if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
            // 扁平当前级别的数组或参数对象,使用递归,每次循环value,将里面的数组调用并平铺
            // flatten current level of array or arguments object
            if (!shallow) value = flatten(value, shallow, strict);
            var j = 0, len = value.length;
            output.length += len;

            // index++ 每次添加一个子集j++的元素
            // 循环子元素,把每一个都添加到父集
            while (j < len) {
                output[idx++] = value[j++];
            }
        } 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);
};
/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * without
 * 返回第一个数组不包含后面参数的值
 * 和difference差不多,只不过把后面的数组变成了参数
 */
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
    // _.difference(array1,array2) 返回array1不存在array2中的值。
    // slice.call(arguments, 1), -> 返回所有参数除去第一个之后的一个数组
    return _.difference(array, slice.call(arguments, 1));
};

/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * uniq & unique
 * 去重
 */
// 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 [];
    // 第二个值必须是布尔值,否则为false,也就是不需要后面自定义排序方法
    if (!_.isBoolean(isSorted)) {
        context = iteratee;
        iteratee = isSorted;
        isSorted = false;
    }
    // 这是处理一个对象的方法,获取对比的属性
    if (iteratee != null) iteratee = cb(iteratee, context);
    var result = [];
    var seen = [];
    // 循环array中的每一项
    for (var i = 0, length = array.length; i < length; i++) {
        // 获取每一项的值,与比较的方式
        var value = array[i],
            computed = iteratee ? iteratee(value, i, array) : value;
        // 如果需要自定义处理,则isSorted为true
        if (isSorted) {
            // 如果i = 0,则 !i = true
            // 如果后面一个值不等于前面一个值,则push,因为已经排序,所以数组类似[1, 1, 3, 5, 5, 6, 6, 

6, 7]
// 所以只要后一个不同于前一个,就push否则跳过就好
if (!i || seen !== computed) result.push(value);
seen = computed;
} else if (iteratee) {
// 是否有条件判断语句
if (!.contains(seen, computed)) {
seen.push(computed);
result.push(value);
}
// 比较数组
.contains(),会返回一个是否包含第二个参数的布尔值
// 如果不包含,则push
} else if (!_.contains(result, value)) {
result.push(value);
}
}
return result;
};

_.uniq(array, [isSorted], [iteratee]);

// array -> 需要处理的数组,
// isSorted -> 是否已经排序
// iteratee -> 是否自定义排序规则



/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * union
 * 提取并集
 */

// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
    // arguments首先将参数合并为数组,
    // flatten 再将数组扁平化
    // 最后使用_.uniq()对数组去重
    return _.uniq(flatten(arguments, true, true));
};


/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * intersection
 * 提取交际
 */

// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
    if (array == null) return [];
    var result = [];
    var argsLength = arguments.length;

    // 循环第一个arrray,结果肯定在第一个array中存在
    for (var i = 0, length = array.length; i < length; i++) {
        var item = array[i];
        // 如果item在result中存在,则跳出本次循环
        if (_.contains(result, item)) continue;
        // 检查第一个array的item 是否在每一个总都存在,否则退出
        for (var j = 1; j < argsLength; j++) {
            if (!_.contains(arguments[j], item)) break;
        }
        // 如果每个都存在,就保存
        // j === argsLength 意思是为了确保上面的循环跑完了。
        if (j === argsLength) result.push(item);
    }
    return result;
};
/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * intersection
 * 类似于without,但返回的值来自array参数数组,并且不存在于other 数组.
 */
// 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) {
    // 现将所有数组扁平化,后面的1值不包含第一个array
    var rest = flatten(arguments, true, true, 1);
    // 然后取出所有真值
    return _.filter(array, function(value){
        // _.contains() 本位比较真值,取反则为假
        return !_.contains(rest, value);
    });
};
/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * zip & unzip
 * 两个差不多,都是讲对应位置的值放在一起,只不过zip是将全部数组放在一个数组中,二unzip是将他们分开成多个
 */
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
    return _.unzip(arguments);
};

// Complement of _.zip. Unzip accepts an array of arrays and groups
// each array's elements on shared indices
_.unzip = function(array) {
    // 兼容具有length属性的对象
    var length = array && _.max(array, 'length').length || 0;
    var result = Array(length);
    for (var index = 0; index < length; index++) {
        // _.pluck(array, key),返回所有数组中包含key值的value
        result[index] = _.pluck(array, index);
    }
    return result;
};

/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * object
 * 将键值对数组转换为json,或者将两个数组,对应index的值转为json
 * 有values值 ,则([a, b, c], [1, 2, 3]) -> {a: 1, b: 2, c: 3}
 * 没有则: ([[a, 2], [b, 2], [c, 3]) -> {a: 1, b: 2, c: 3}
 */
// 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.
_.object = function(list, values) {
    var result = {};
    for (var i = 0, length = list && list.length; i < length; i++) {
        if (values) {
            // 如果有values则匹配相同的index
            result[list[i]] = values[i];
        } else {
            // 如果只有一个参数,则匹配二维数组的第一个值个第二个值
            result[list[i][0]] = list[i][1];
        }
    }
    return result;
};
/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * indexOf & lastIndexOf
 *  查找索引
 */
// 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.
_.indexOf = function(array, item, isSorted) {
    var i = 0, length = array && array.length;

    if (typeof isSorted == 'number') {
        // 正: 则为本身,负: 则减去它
        i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted;
    } else if (isSorted && length) {
        // _.sortedIndex(array, item); 将item插入array,能保障排序, i标示插入的索引
        // 其实就是把他插入本来应该在的位置,但是比较的时候却没有,所以就是-1
        i = _.sortedIndex(array, item);
        return array[i] === item ? i : -1;
    }
    // 如果是NaN ,findIndex寻找NaN的位置 -> 数字一定要判断NaN的情况
    if (item !== item) {
        return _.findIndex(slice.call(array, i), _.isNaN);
    }
    for (; i < length; i++) if (array[i] === item) return i;
    return -1;
};

_.lastIndexOf = function(array, item, from) {
    var idx = array ? array.length : 0;
    // idx不能大于length-1
    if (typeof from == 'number') {
        idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1);
    }
    // NaN
    if (item !== item) {
        return _.findLastIndex(slice.call(array, 0, idx), _.isNaN);
    }

    // --idx = length-1 ,如果有一个相等,则输出,否则返回 -1
    while (--idx >= 0) if (array[idx] === item) return idx;
    return -1;
};
/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * findIndex & findLastIndex
 * 类似于_.indexOf ,只不过indexOf给的是元素,而这个需要表达式
 */
// Generator function to create the findIndex and findLastIndex functions
function createIndexFinder(dir) {
    return function(array, predicate, context) {
        predicate = cb(predicate, context);
        var length = array != null && array.length;
        var index = dir > 0 ? 0 : length - 1;
        for (; index >= 0 && index < length; index += dir) {
            // 如果回调函数为true,则输出index
            if (predicate(array[index], index, array)) return index;
        }
        return -1;
    };
}

// Returns the first index on an array-like that passes a predicate test
_.findIndex = createIndexFinder(1);

_.findLastIndex = createIndexFinder(-1);

/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * sortedIndex
 * 使用二分查找确定value在list中的位置序号
 */
// 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.
_.sortedIndex = function(array, obj, iteratee, context) {
    // 检查iteratee是否存在,是否为对象
    iteratee = cb(iteratee, context, 1);
    var value = iteratee(obj);
    var low = 0, high = array.length;

    // 每一次都拿中间值与目标比较,mid+ 1 取后部分,或者high = mid 取前部分,以此类推
    while (low < high) {
        var mid = Math.floor((low + high) / 2);
        if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
    }
    return low;
};

/*—————————————————————————————————————————————————————————

—————————————————*/

/**
 * range
 * 一个用来创建整数灵活编号的列表的函数
 */

// 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 (arguments.length <= 1) {
        stop = start || 0;
        start = 0;
    }
    step = step || 1;
    // ceil 向上取整
    // 创建数组及计算数组个数
    var length = Math.max(Math.ceil((stop - start) / step), 0);
    var range = Array(length);

    // start 每次都加 step
    for (var idx = 0; idx < length; idx++, start += step) {
        range[idx] = start;
    }

    return range;
};
上一篇下一篇

猜你喜欢

热点阅读