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

前言

集合相关函数,逻辑处理。

// 查找并返回最大的元素
_.max = function (obj, iteratee, context) {
    var result = -Infinity, lastComputed = -Infinity,
        value, computed;
    // 如果 iteratee 为 undefined 或者 null 或者 iteratee 为 number + 数组的第一个元素不是对象(即数组元素为除了对象以外的其它数据类型) + obj 不为 null,
    if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
        obj = isArrayLike(obj) ? obj : _.values(obj);
        for (var i = 0, length = obj.length; i < length; i++) {
            value = obj[i];
            if (value != null && value > result) {
                // 如果当前遍历的值大于 result 则把 value 赋值给 result
                result = value;
            }
        }
    } else {
        // 否则使用 _.each 方法遍历 obj 对象并比较 iteratee 的返回值
        iteratee = cb(iteratee, context);
        _.each(obj, function (v, index, list) {
            // 保存根据传入的 iteratee 回调中返回的值
            computed = iteratee(v, index, list);
            if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
                result = v;
                lastComputed = computed;
            }
        });
    }
    return result;
};


// 查找并返回最小的元素
_.min = function (obj, iteratee, context) {
    var result = Infinity, lastComputed = Infinity,
        value, computed;
    if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
        obj = isArrayLike(obj) ? obj : _.values(obj);
        for (var i = 0, length = obj.length; i < length; i++) {
            value = obj[i];
            if (value != null && value < result) {
                result = value;
            }
        }
    } else {
        iteratee = cb(iteratee, context);
        _.each(obj, function (v, index, list) {
            computed = iteratee(v, index, list);
            if (computed < lastComputed || computed === Infinity && result === Infinity) {
                result = v;
                lastComputed = computed;
            }
        });
    }
    return result;
};


// 返回一个随机乱序的数组
_.shuffle = function (obj) {
    return _.sample(obj, Infinity);
};


// 随机返回一个值,或者返回一个乱序的数组
_.sample = function (obj, n, guard) {
    if (n == null || guard) {
        if (!isArrayLike(obj)) obj = _.values(obj);
        // 这里需要知道_.random 的实现,如果只有一个参数,则这个参数为最大值,最小值为 0
        return obj[_.random(obj.length - 1)];
    }
    var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
    var length = getLength(sample);
    n = Math.max(Math.min(n, length), 0);
    var last = length - 1;
    for (var index = 0; index < n; index++) {
        // 在 index 和 last 之间生成一个随机数
        var rand = _.random(index, last);
        // 下标为 index 与 下标为 rand 的项互换
        var temp = sample[index];
        sample[index] = sample[rand];
        sample[rand] = temp;
    }
    return sample.slice(0, n);
};


// _.sortBy 是对 sort() 方法的二次封装
// 方法中用到了 _.map() 作前置处理,再使用 sort() 方法排序,最后通过 _.pluck() 获取数组中对象元素的 key 为 value 的值
_.sortBy = function (obj, iteratee, context) {
    var index = 0;
    iteratee = cb(iteratee, context);
    return _.pluck(_.map(obj, function (value, key, list) {
        return {
            value: value,
            index: index++,
            criteria: iteratee(value, key, list)
        };
    }).sort(function (left, right) {
        var a = left.criteria;
        var b = right.criteria;
        // 如果不等于 a !== b
        if (a !== b) {
            if (a > b || a === void 0) return 1;
            if (a < b || b === void 0) return -1;
        }
        // 否则根据下标进行排序
        return left.index - right.index;
    }), 'value');
};


// 分组统一处理函数
var group = function (behavior, partition) {
    // 返回一个分组函数,
    return function (obj, iteratee, context) {
        var result = partition ? [[], []] : {};
        iteratee = cb(iteratee, context);
        _.each(obj, function (value, index) {
            // 生成 key 值
            var key = iteratee(value, index, obj);
            // 按不同的规则把值归类
            behavior(result, value, key);
        });
        return result;
    };
};


// 根据不同的 key 进行归类
_.groupBy = group(function (result, value, key) {
    if (has(result, key)) result[key].push(value); else result[key] = [value];
});


// 根据 key 值进行归类
_.indexBy = group(function (result, value, key) {
    result[key] = value;
});


// 统计 key 出现的次数,这个 key 是 group() 方法中 iteratee(value, index, obj) 的返回值,
// 即调用  _.countBy() 方法时传入的 iteratee 函数。
_.countBy = group(function (result, value, key) {
    if (has(result, key)) result[key]++; else result[key] = 1;
});


var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;


// 转化成数组
_.toArray = function (obj) {
    if (!obj) return [];
    // 如果传入的是一个数组,那么返回一个浅拷贝
    if (_.isArray(obj)) return slice.call(obj);
    // 如果是字符串
    if (_.isString(obj)) {
        // Keep surrogate pair characters together
        // 返回匹配指定区域的 unicode 字符把组成的数组
        return obj.match(reStrSymbol);
    }
    // 如果是一个类数组,
    // _.identity() 方法返回的是对象的值
    if (isArrayLike(obj)) return _.map(obj, _.identity);
    // 如果前面都没符合,则当作对象处理,使用 _.values(obj) 处理,返回对象的所有值组成的一个数组
    return _.values(obj);
};


// 返回对象的大小(数组返回长度,对象返回 key 值的数量).
_.size = function (obj) {
    if (obj == null) return 0;
    return isArrayLike(obj) ? obj.length : _.keys(obj).length;
};


// 根据一定的规则把数组分成两组([[], []])
_.partition = group(function (result, value, pass) {
    result[pass ? 0 : 1].push(value);
}, true);