Underscore.js 源码分析(第四出)

前言

数组相关

// 返回数组的第一个元素
_.first = _.head = _.take = function (array, n, guard) {
    // 如果 array undefined|null 或者 array 的长度小于 1,则根据 n 返回 undefined 或者空数组
    if (array == null || array.length < 1) return n == null ? void 0 : [];
    // 如果 n 为 undefined|null,或者 guard 返回数组的第一个元素
    if (n == null || guard) return array[0];
    // 如果前面的都不符合(有传 array,n 不为 undefined|null,或者 guard 不为真)
    return _.initial(array, array.length - n);
};

// 如果 n 为 undefined|null ,或者有传 n 并且 guard 值为真,则返回除了最后一个元素的所有元素,
// 如果有传 n 则返回第一个开始的 n 元素
_.initial = function (array, n, guard) {
    return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
};

// 返回数组的最后一个元素
_.last = function (array, n, guard) {
    if (array == null || array.length < 1) return n == null ? void 0 : [];
    // 如果 n 为 undefined|null,或者 guard 返回数组的最后一个元素
    if (n == null || guard) return array[array.length - 1];
    return _.rest(array, Math.max(0, array.length - n));
};

// 返回从指定下标及以后的所有元素
_.rest = _.tail = _.drop = function (array, n, guard) {
    // 如果 n 为 undefined|null,或者 guard 为真,取除了第一个的其它所有元素,
    // 如果 n 不为 undefined|null,则返回 n 及 n 以后的所有元素
    return slice.call(array, n == null || guard ? 1 : n);
};

// 根据 Boolean() 判断值的真假来过滤元素所组成的数组副本
// undefined、null、-0、+0、NaN、''(空字符串),除了这些为假之外其它的 Boolean 处理后都为真
_.compact = function (array) {
    return _.filter(array, Boolean);
};


var flatten = function (input, shallow, strict, output) {
    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))) {
            // 如果 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;
};

// 归递打平一个数组,或者 shallow 为真,只打平第一级
_.flatten = function (array, shallow) {
    return flatten(array, shallow, false);
};

// 返回 array 中不函数 otherArrays 的元素组成的数组
// otherArrays 为 _.without() 中除了第一个参数外的其它参数所组成的数组
_.without = restArguments(function (array, otherArrays) {
    // 返回_.difference() 的执行结果
    return _.difference(array, otherArrays);
});

// 去重
_.uniq = _.unique = function (array, isSorted, iteratee, context) {
    // 如果不为布尔值
    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) { // 如果 array 是已经排好序的数组
            // 如果前后不等就直接入到结果数组中
            if (!i || seen !== computed) result.push(value);
            // 把当前值赋值给 seen 作为前一个值
            seen = computed;
        } else if (iteratee) {
            // 如果当前值 computed 不存在于 seen 中
            // 则把它放到 seen 以及 result 中
            if (!_.contains(seen, computed)) {
                // seen 保存的是 iteratee 的计算值,这个值是去重的依据
                seen.push(computed);
                result.push(value);
            }
        } else if (!_.contains(result, value)) {
            result.push(value);
        }
    }
    return result;
};

// 把数组减少一维后去重
_.union = restArguments(function (arrays) {
    // 返回从摆平(减少一维)的数组中去重数组
    return _.uniq(flatten(arrays, true, true));
});

// 返回传入 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++) {
            // 如果第一个数组中的当前值 item 不存在于arguments[j]数组中则直接跳出循环
            if (!_.contains(arguments[j], item)) break;
        }
        // 如果能顺利遍历完成,说明都通过了检测,item 为所有数组共用的元素
        if (j === argsLength) result.push(item);
    }
    return result;
};

// 返回存在于 array 但不存在于 rest 中的元素所组成的组织
_.difference = restArguments(function (array, rest) {
    // 把 rest 收藏的数组都提到 rest 上,摆平二层数组如:[[1],[2,3]] => [1,2,3]
    rest = flatten(rest, true, true);
    return _.filter(array, function (value) {
        // 返回不存在 rest 数组中的值
        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);


// 把对象转为数组,
_.object = function (list, values) {
    var result = {};
    for (var i = 0, length = getLength(list); i < length; i++) {
        if (values) { // values 存在为 key 单独组成一个数组,值也单独组成一个数组
            result[list[i]] = values[i];
        } else { // 否则为一个二维数组,里面的子数组包含了 key 和 值
            result[list[i][0]] = list[i][1];
        }
    }
    return result;
};

// 查找元素返回第一个符合的下标
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;
    };
};

// 通过 createPredicateIndexFinder 产生不同方向的查找函数
_.findIndex = createPredicateIndexFinder(1);
_.findLastIndex = createPredicateIndexFinder(-1);

// 在已排序的数组中查找 obj 的位置
_.sortedIndex = function (array, obj, iteratee, context) {
    // 返回 iteratee.call(context, value)
    iteratee = cb(iteratee, context, 1);
    // 返回经 iteratee 处理后的值
    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;
};

// 下标查找的共用逻辑
var createIndexFinder = function (dir, predicateFind, sortedIndex) {
    return function (array, item, idx) { // idx [Boolean|number]
        var i = 0, length = getLength(array);
        // idx 为 number 类型
        // 这个 if 语句为计算 i 和 length 的值
        if (typeof idx == 'number') {
            if (dir > 0) { // _.indexOf() 方法调用
                // 求出开始下标
                i = idx >= 0 ? idx : Math.max(idx + length, i);
            } else { // 否则 _.lastIndexOf() 方法调用
                length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
            }
            // _.indexOf() 方法调用
        } else if (sortedIndex && idx && length) { // idx 为布尔值并为 true
            // 使用 sortedIndex 的二分法找到 item 在 array 中的下标
            idx = sortedIndex(array, item);
            // 如果 array[idx] === item 则说明 item 值在 array 中存在,即找到
            return array[idx] === item ? idx : -1;
        }
        // 如果 item 不等于自身,说明 item 为 NaN
        if (item !== item) {
            // 找到 item 在数组中的位置
            idx = predicateFind(slice.call(array, i, length), _.isNaN);
            return idx >= 0 ? idx + i : -1;
        }
        // idx 的值根据 dir 的正负来取 i 或者 length - 1
        //
        for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
            if (array[idx] === item) return idx;
        }
        return -1;
    };
};

// 下标查找(从左边开始)
_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
// 下标查找(从右边开始)
_.lastIndexOf = createIndexFinder(-1, _.findLastIndex);

// 生成一个指定范围内的数组
_.range = function (start, stop, step) {
    // 如果 stop 为 null|undefined,初始化 stop 和 start
    if (stop == null) {
        stop = start || 0;
        start = 0;
    }
    // 如果 step 不为真
    if (!step) {
        // 若 stop 小于 start 则 step 取 -1,否则为取 1
        step = stop < start ? -1 : 1;
    }
    // 计算出数组的长度
    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;
};

// 把数组分成多个数组,每个数组的最大长度为 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));
    }
    return result;
};
声明

1.原创文章,不经本站同意,不得以任何形式转载,如有不便,请多多包涵!

2.本文永久链接:http://yunkus.com/post/5d8b313629edaa21

3.如果觉得本文对你有帮助,或者解决了你的问题,不妨扫一扫右边的二维码打赏支持,你的一分一毫,可能会让世界变得更美好。

微信
扫一扫,赏我
支付宝
扫一扫,赏我