Underscore源码(2)

Collection Functions

each & map

_.each = _.forEach = function(obj, iteratee, context) {
    // 查看是否有第三个参数
    iteratee = optimizeCb(iteratee, context); // iterator
    var i, length;
    // 如果他有length
    if (isArrayLike(obj)) {
        for (i = 0, length = obj.length; i < length; i++) {
            // iteratee 是用户自定义的回调函数的一个包装
            iteratee(obj[i], i, obj);
        }
    } else {
        // 不是arr 就是json,然后取出它的key,然后循环
        var keys = _.keys(obj);
        for (i = 0, length = keys.length; i < length; i++) {
            iteratee(obj[keys[i]], keys[i], obj);
        }
    }
    // 返回obj是为了下一次的链式调用
    return obj;
};
// value: 每一个参数
// index: 索引
// collection: arr本身
const each = _.each([0, 1, 2, 3, 4], (value, index, collection) => console.log(value));
// 0 -> 1-> 2 -> 3 -> 4
// map 和each差不多

_.map = _.collect = function(obj, iteratee, context) {
    // 迭代自身,和each一样,
    iteratee = cb(iteratee, context);
    // 检查是否是数组,是否含有key
    var keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length,
        results = Array(length);
    for (var index = 0; index < length; index++) {
        var currentKey = keys ? keys[index] : index;
        // iteratee同样是用户自定义的函数
        results[index] = iteratee(obj[currentKey], currentKey, obj);
    }
    return results;
};
const map = _.map([0, 1, 2, 3, 4], (value, index, collection) => console.log(value));
// 0 -> 1-> 2 -> 3 -> 4

reduce & reduceRight

// Create a reducing function iterating left or right.
function createReduce(dir) {
    // Optimized iterator function as using arguments.length
    // in the main function will deoptimize the, see #1991.
    function iterator(obj, iteratee, memo, keys, index, length) {
        for (; index >= 0 && index < length; index += dir) {
            var currentKey = keys ? keys[index] : index;
            // 如果有第三个参数,则memo就是初始值,否则就是obj的第一个参数
            memo = iteratee(memo, obj[currentKey], currentKey, obj);
        }
        return memo;
    }

    return function(obj, iteratee, memo, context) {
        // 迭代
        iteratee = optimizeCb(iteratee, context, 4);
        // 取出keys length index
        var keys = !isArrayLike(obj) && _.keys(obj),
            length = (keys || obj).length,
            index = dir > 0 ? 0 : length - 1; // 正序还是倒序
        // 如果只有两个参数,判断是数组还是json
        // Determine the initial value if none is provided.
        // 查看是否给了初始值,
        if (arguments.length < 3) {
            // 判断是数组还是json并取值。
            memo = obj[keys ? keys[index] : index];
            // 获取初始index > 如果从左开始,index = 1 ,否则等于length -1
            index += dir;
        }
        return iterator(obj, iteratee, memo, keys, index, length);
    };
}

// **Reduce** builds up a single result from a list of values, aka `inject`, or `foldl`.
_.reduce = _.foldl = _.inject = createReduce(1);

// The right-associative version of reduce, also known as `foldr`.
_.reduceRight = _.foldr = createReduce(-1);

// 两个参数的时候,累加数组,当有第三个参数时,则是将第三个参数当做初始值
var reduceRight = _.reduceRight(
    [0, 1, 2, 3, 4],
    function(memo, value , index, list){
        return memo + value
    }, 10);
console.log(reduceRight);
// 20

find

// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) {
    var key;
    if (isArrayLike(obj)) {
        key = _.findIndex(obj, predicate, context);
    } else {
        key = _.findKey(obj, predicate, context);
    }
    if (key !== void 0 && key !== -1) return obj[key];
};

filter and reject

// Return all the elements that pass a truth test.
// Aliased as `select`.
_.filter = _.select = function(obj, predicate, context) {
    var results = [];
    predicate = cb(predicate, context);
    _.each(obj, function(value, index, list) {
        // 便利obj 的每一个值,然后返回predicate为真的值
        if (predicate(value, index, list)) results.push(value);
    });
    return results;
};

// 取predicate为真的值
var filter = _.filter([2, 0, 1, 3, 4], function(value, index, list) {
    return value != index
});
console.log(filter);
// [2, 0, 1]

// 取一个表达式的否定值
// Returns a negated version of the passed-in predicate.
_.negate = function(predicate) {
    return function() {
        return !predicate.apply(this, arguments);
    };
};

// 将filter的第二个值设为否定值,则每次返回的都是取反
// reject: 拒绝
// negate: 否定 a => !a
// Return all the elements for which a truth test fails.
_.reject = function(obj, predicate, context) {
    return _.filter(obj, _.negate(cb(predicate)), context);
};

// 取predicate为真的值
var reject = _.reject([2, 0, 1, 3, 4], function(value, index, list){return value != index});
console.log(reject);
// [3, 4]

every and some

/**
 * Determine: 确定
 * whether: 是否
 * match: 使相配/相同
 * truth test: 真理 检验
 * Aliased: 别名
 */
// Determine whether all of the elements match a truth test. Aliased as `all`.
// 所有内容都必须符合条件,否则为假
_.every = _.all = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var keys = !isArrayLike(obj) && _.keys(obj), // arr、json
        length = (keys || obj).length;
    for (var index = 0; index < length; index++) {
        var currentKey = keys ? keys[index] : index;
        // 如果里面有一个不为真,则返回false
        if (!predicate(obj[currentKey], currentKey, obj)) return false;
    }
    return true;
};
var every = _.every([0, 1, 2, 3], (value, key, list) => value < 3)
console.log(every)
// false

// Determine if at least one element in the object matches a truth test. Aliased as `any`.
// 只要有一个符合条件,则为真
_.some = _.any = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length;
    for (var index = 0; index < length; index++) {
        var currentKey = keys ? keys[index] : index;
        // 只要一个符合条件,则为真
        if (predicate(obj[currentKey], currentKey, obj)) return true;
    }
    return false;
};

var sume = _.some([10, 9, 5, 7], (value, key, list) => value < 7)
console.log(sume)
// true

contains

// Determine if the array or object contains a given value (using `===`).
// Aliased as `includes` and `include`.
// 目标内容是否存在  fromIndex 是从什么位置开始查找 包括位置自身
_.contains = _.includes = _.include = function(obj, target, fromIndex) {
    // 如果股市数组,就把里面的值全部取出来,变相的变成数组
    if (!isArrayLike(obj)) obj = _.values(obj);
    // obj = ['aaa', 'bbb']
    return _.indexOf(obj, target, typeof fromIndex == 'number' && fromIndex) >= 0;
};

var sumc = _.contains({a: 'aaa', b: 'bbb'}, 'bbb', 1); // 1 是起始位置 是_.indexOf()的第三个参数
console.log(sumc);
// true

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

/**
 * invoke
 */
// Invoke a method (with arguments) on every item in a collection.
// 在list的每个元素上执行methodName方法。 任何传递给invoke的额外参数,invoke都会在调用methodName方法的时候传递给它。
// 目标内容是否存在  fromIndex 是从什么位置开始查找 包括位置自身
_.invoke = function(obj, method) {
    //invoke方法接受多于两个参数作为函数参数,从第3个参数开始将作为被调用函数的参数
    var args = slice.call(arguments, 2);
    var isFunc = _.isFunction(method);
    return _.map(obj, function(value) {
        // 这里判断用户定义的是方法,还是方法名
        var func = isFunc ? method : value[method];
        return func == null ? func : func.apply(value, args);
    });
};
console.log( _.invoke([[5, 1, 7], [3, 2, 1]], 'reverse'));
// [[7, 1, 5], [1, 2, 3]]

// splice 可以传参数,则放在后面
var sums = _.invoke([[5, 1, 7], [3, 2, 1]], 'splice', 2);
console.log(sums);
// [[7], [1]]


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

/**
 * pluck
 * 也许是map最常使用的用例模型的简化版本,即萃取数组对象中某属性值,返回一个数组。
 */
// 返回obj的每一个key的值
_.property = function(key) {
    return function(obj) {
        return obj == null ? void 0 : obj[key];
    };
};
// 萃取json模型对象中的某个特定值
_.pluck = function(obj, key) {
    return _.map(obj, _.property(key));
};

// 发现每次看到这种return 函数的方式都有点梦,然后把它扔回去,瞬间感觉好理解多了。
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
    return _.map(obj, _.property(key));
};



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

/**
 * where and findWhere
 * 根据属性查找obj中的位置
 */


_.matcher = _.matches = function(attrs) {
    // 将attrs 合并到{}
    attrs = _.extendOwn({}, attrs);
    return function(obj) {
        return _.isMatch(obj, attrs);
    };
};
// 告诉你attrs中的键和值是否包含在object中。
_.isMatch = function(object, attrs) {
    var keys = _.keys(attrs), length = keys.length;
    if (object == null) return !length;
    var obj = Object(object);
    for (var i = 0; i < length; i++) {
        var key = keys[i];
        // 两个的键值对不相等,或者obj中没有这个key ,则返回false
        if (attrs[key] !== obj[key] || !(key in obj)) return false;
    }
    return true;
};

// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
_.where = function(obj, attrs) {
    return _.filter(obj, _.matcher(attrs));
};

// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_.findWhere = function(obj, attrs) {
    return _.find(obj, _.matcher(attrs));
};

var stges = [
    {name: 'moe', age: 40, height: 56, size: 23},
    {name: 'larry', age: 50},
    {name: 'moe', age: 40, family: 'english'},
    {name: 'curly', age: 60}
];
var cum = _.where(stges, {name: "moe", age: 40});
console.log(cum);
// [{name: 'moe', age: 40, height: 56, size: 23},{name: 'moe', age: 40, family: 'english'}]

var tom = _.findWhere(stges, {name: "moe", age: 40});
console.log(tom);
// [{name: 'moe', age: 40, height: 56, size: 23}]



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


/**
 * max and min
 */

    // 根据比较规则,取无穷大。
// Return the maximum element (or element-based computation).
_.max = function(obj, iteratee, context) {
    var result = -Infinity, lastComputed = -Infinity,
        value, computed;
    if (iteratee == null && obj != null) {
        obj = isArrayLike(obj) ? obj : _.values(obj);
        for (var i = 0, length = obj.length; i < length; i++) {
            value = obj[i];
            // 如果value大于result 则让result = value ,然后再与下一个比较
            if (value > result) {
                result = value;
            }
        }
    } else {
        iteratee = cb(iteratee, context);
        _.each(obj, function(value, index, list) {
            // computed 是比较函数返回的值,如果比lastComputed 大,则将值赋给lastComputed,然后在于下一个比较
            computed = iteratee(value, index, list);
            if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
                result = value;
                lastComputed = computed;
            }
        });
    }
    return result;
};


var stooges = [
    {name: 'moe', age: 40, height: 56, size: 23},
    {name: 'larry', age: 50},
    {name: 'moe', age: 40, family: 'english'},
    {name: 'curly', age: 60}
];
var sum = _.max(stooges, function(obj){
    return obj.age
});
console.log(sum);


// Return the minimum element (or element-based computation).
// 和max差不多
_.min = function(obj, iteratee, context) {
    var result = Infinity, lastComputed = Infinity,
        value, computed;
    if (iteratee == null && obj != null) {
        obj = isArrayLike(obj) ? obj : _.values(obj);
        for (var i = 0, length = obj.length; i < length; i++) {
            value = obj[i];
            if (value < result) {
                result = value;
            }
        }
    } else {
        iteratee = cb(iteratee, context);
        _.each(obj, function(value, index, list) {
            computed = iteratee(value, index, list);
            if (computed < lastComputed || computed === Infinity && result === Infinity) {
                result = value;
                lastComputed = computed;
            }
        });
    }
    return result;
};

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

/**
 * shuffle
 * 输出一个随机排序的函数
 */
// Shuffle a collection, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
_.shuffle = function(obj) {
    var set = isArrayLike(obj) ? obj : _.values(obj);
    var length = set.length;
    var shuffled = Array(length);
    for (var index = 0, rand; index < length; index++) {
        // 返回一个0 到index 之间的随机数
        rand = _.random(0, index);
        // 如果随机数不等一index ,则将shuffled就等于shuffled的第rand个

        // 比如说第五次,index = 5, rand 为 3
        // shuffled[5] = shuffled[3]
        // shuffled[3] = set[5]
        // 算是交换了个一个值,并且消除了重复的值
        // 使用了Fisher-Yates 乱序算法

        if (rand !== index) shuffled[index] = shuffled[rand];
        shuffled[rand] = set[index];
    }
    return shuffled;
};
/*——————————————————————————————————————————————————————————————————————————*/

/**
 * sample
 * 输出一个或多个随机值
 */
// Sample **n** random values from a collection.
// If **n** is not specified, returns a single random element.
// The internal `guard` argument allows it to work with `map`.
_.sample = function(obj, n, guard) {
    // 如果n 不存在,就返回一个随机值
    if (n == null || guard) {
        // 如果不是arry,就将obj装换为数组
        if (!isArrayLike(obj)) obj = _.values(obj);
        // 取一个随机值
        return obj[_.random(obj.length - 1)];
    }
    // 在使用乱序算法的基础上,使用slice方法获取n个
    return _.shuffle(obj).slice(0, Math.max(0, n));
};
/*——————————————————————————————————————————————————————————————————————————*/

/**
 * sortBy
 * 排序
 */
// 按照特定的方式排序
// Sort the object's values by a criterion produced by an iteratee.
_.sortBy = function(obj, iteratee, context) {
    iteratee = cb(iteratee, context);
    // 先用pluck 取出value的值,然后使用iteratee排序
    return _.pluck(_.map(obj, function(value, index, list) {
        return {
            value: value,
            index: index,
            // iteratee如果是function 则执行func, 如果不存在则返回value本身
            // 可以查看cd()函数,value == null => _.identity;
            criteria: iteratee(value, index, list)
        };
    }).sort(function(left, right) {
        /// 排序
        var a = left.criteria;
        var b = right.criteria;
        if (a !== b) {
            if (a > b || a === void 0) return 1; // 大于
            if (a < b || b === void 0) return -1; // 小于
        }
        return left.index - right.index; // 等于
        // 最后是使用pluck 的方法,将value的值输出出来。
    }), 'value');
};
/*——————————————————————————————————————————————————————————————————————————*/

/**
 * groupBy & indexBy & countBy
 * 拆分集合
 */
// An internal function used for aggregate "group by" operations.
var group = function(behavior) {
    return function(obj, iteratee, context) {
        var result = {};
        iteratee = cb(iteratee, context);
        _.each(obj, function(value, index) {
            // 调用自定义的方法返回一个key来拆分集合
            var key = iteratee(value, index, obj);
            // 定义不同的输出结果
            behavior(result, value, key);
        });
        return result;
    };
};

// 把一个集合分组为多个集合,通过自定义函数返回的结果进行分组. 如果自定义函数是一个字符串而不是函数, 那么将使用 iterator 作为各元素的属性名来对比进行分组.
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = group(function(result, value, key) {
    // 如果存在,就将key作为键,如果没有,就将每一个都push到这个key里面
    if (_.has(result, key)) result[key].push(value); else result[key] = [value];
});

//和groupBy非常像,但是当你知道你的键是唯一的时候可以使用indexBy 。
// Indexes the object's values by a criterion, similar to `groupBy`, but for
// when you know that your index values will be unique.
_.indexBy = group(function(result, value, key) {
    // 使用key来拆分,并把key作为每一项的key
    result[key] = value;
});

// 类似于indexby,但是不返回列表的值,只返回结果的个数
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_.countBy = group(function(result, value, key) {
    if (_.has(result, key)) result[key]++; else result[key] = 1;
});
/*——————————————————————————————————————————————————————————————————————————*/

/**
 * toArray
 * 转成array
 */
// Safely create a real, live array from anything iterable.
_.toArray = function(obj) {
    if (!obj) return [];// 确定存在
    if (_.isArray(obj)) return slice.call(obj); // 如果是一个一个arry,则用slice拆分
    if (isArrayLike(obj)) return _.map(obj, _.identity); //如果有length,用map重新返回自身
    return _.values(obj); //如果是json,则取出每一个值
};
/*——————————————————————————————————————————————————————————————————————————*/

/**
 * size
 * 返回集合的长度
 */
// Return the number of elements in an object.
_.size = function(obj) {
    if (obj == null) return 0;
    // 如果有length属性,则直接输出length,如果是json,则取出所有的key,然后求length
    return isArrayLike(obj) ? obj.length : _.keys(obj).length;
};

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

/**
 * partition
 * 返回两个集合,一个是符合的,一个是不符合的。
 */
// Split a collection into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
_.partition = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var pass = [], fail = [];
    _.each(obj, function(value, key, obj) {
        // (predicate(value, key, obj) 为true 则pass.push 否则 fail.push

        (predicate(value, key, obj) ? pass : fail).push(value);
    });
    return [pass, fail];
};

你可能感兴趣的:(Underscore源码(2))