美文网首页
Underscore源码(2)

Underscore源码(2)

作者: ____雨歇微凉 | 来源:发表于2016-12-09 23:02 被阅读26次

    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)

          本文链接:https://www.haomeiwen.com/subject/ogiupttx.html