underscore.js 源码学习 collection

栏目: JavaScript · 发布时间: 5年前

内容简介:// Internal function that returns an efficient (for current engines) version// of the passed-in callback, to be repeatedly applied in other Underscore// functions.

// Internal function that returns an efficient (for current engines) version

// of the passed-in callback, to be repeatedly applied in other Underscore

// functions.

// 内部复用函数,用于改变函数上下文。

var optimizeCb = function(func, context, argCount) {
    //context为undefined情况下,直接返回func
    // void 0
    // 执行过程:1,对右侧表达式求值。2.返回undefined
    // 为何这样多此一举:js中,undefined不是保留字,可以创建windows.undefined=anything.void返回undefined较安全。
    // 用处:1返回undefined。2a标签不跳转href=javascript:(void 0)3img空图片src=javascript:(void 0)
    if (context === void 0) return func;
    //有传入context情况下,将func上下文改为context
    switch (argCount == null ? 3 : argCount) {
        case 1:
            return function(value) {
                return func.call(context, value);
            };
            // The 2-argument case is omitted because we’re not using it.
        case 3:
            return function(value, index, collection) {
                return func.call(context, value, index, collection);
            };
            //_.reduce中使用
        case 4:
            return function(accumulator, value, index, collection) {
                return func.call(context, accumulator, value, index, collection);
            };
    }
    return function() {
        return func.apply(context, arguments);
    };
};
var builtinIteratee;

// An internal function to generate callbacks that can be applied to each

// element in a collection, returning the desired result — either identity ,

// an arbitrary callback, a property matcher, or a property accessor.

// 内部函数,生成适用于集合每个元素的回调函数

// 传入null等空值,得到等价函数

// 传入function,得到绑定了上下文的函数

// 传入对象,得到匹配函数

// 传入其他,得到属性访问函数

var cb = function(value, context, argCount) {
    //??是何用意??
    if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
    //返回null
    if (value == null) return _.identity;
    //如果是函数,绑定函数上下文
    if (_.isFunction(value)) return optimizeCb(value, context, argCount);
    //如果是对象,返回匹配函数
    if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
    //返回获取属性值函数
    return _.property(value);
};

// External wrapper for our callback generator. Users may customize

// _.iteratee if they want additional predicate/iteratee shorthand styles.

// This abstraction hides the internal-only argCount argument.

// 外部函数,返回cb函数结果

_.iteratee = builtinIteratee = function(value, context) {
    return cb(value, context, Infinity);
};

// Some functions take a variable number of arguments, or a few expected

// arguments at the beginning and then a variable number of values to operate

// on. This helper accumulates all remaining arguments past the function’s

// argument length (or an explicit startIndex ), into an array that becomes

// the last argument. Similar to ES6’s "rest parameter".

// 用于实参个数不定的函数的固定参数及剩余参数整理。

// example:_.invoke

// func.length=3,so startIndex=2,即从第三个参数开始,都存入rest变量中,并作为第三个参数传入。第一二个参数不变。

// example:_.without

// func.length=2,so startIndex=1,即从第二个参数开始,都存入rest变量中,并作为第二个参数传入第一个参数不变。

// example:_.union

// func.length=1,so startIndex=0,即将所有参数都传入rest变量中,并作为第一个参数传入。

// 最后一种情况,func形参个数大于3,不再使用call手动传入参数

// 而是利用apply的数组参数特性,将参数保存在数组中,再传入函数。

var restArguments = function(func, startIndex) {
    console.log('func2')
    //func.length->形参个数        
    startIndex = startIndex == null ? func.length - 1 : +startIndex;
    return function() { //func3
        // arguments.length->实参个数
        // 个人理解:此处arguments指的是最贴近的这个闭包function在执行时传入的参数,并不是func在执行时传入的参数个数。
        console.log('func3')
        var length = Math.max(arguments.length - startIndex, 0),
            rest = Array(length),
            index = 0;
        for (; index < length; index++) {
            rest[index] = arguments[index + startIndex];
        }
        switch (startIndex) {
            case 0:
                return func.call(this, rest);
            case 1:
                return func.call(this, arguments[0], rest);
            case 2:
                return func.call(this, arguments[0], arguments[1], rest);
        }
        var args = Array(startIndex + 1);
        for (index = 0; index < startIndex; index++) {
            args[index] = arguments[index];
        }
        args[startIndex] = rest;
        return func.apply(this, args);
    };
};

// Invoke a method (with arguments) on every item in a collection.

// 在集合的每个元素上调用方法path,参数为arguments

// _.invoke([[3,4,5],[65,33,3]],'sort')函数运行顺序

// 在underscore.js加载编译时,restArgument被执行,console func2。并返回匿名函数func3

// 在_.invoke()被执行,即匿名函数func3被执行,console func3。此时的arguments=[[[3,4,5],[65,33,3]],'sort']。返回匿名函数func执行的值

// 执行func,console func。参数obj = arguments[0]=[[[3,4,5],[65,33,3]],path = arguemnts[1]='sort',args = rest = []

// console func4

// console func4

_.invoke = restArguments(function(obj, path, args) { //func
    console.log('func')
    var contextPath, func;
    if (_.isFunction(path)) {
        func = path;
    } else if (_.isArray(path)) {
        contextPath = path.slice(0, -1);
        path = path[path.length - 1];
    }
    return _.map(obj, function(context) { //func4
        console.log('func4')
        var method = func;
        if (!method) {
            if (contextPath && contextPath.length) {
                context = deepGet(context, contextPath);
            }
            if (context == null) return void 0;
            method = context[path];
        }
        return method == null ? method : method.apply(context, args);
    });
});

// An internal function for creating a new object that inherits from another.

var baseCreate = function(prototype) {
    if (!_.isObject(prototype)) return {};
    if (nativeCreate) return nativeCreate(prototype);
    Ctor.prototype = prototype;
    var result = new Ctor;
    Ctor.prototype = null;
    return result;
};

// 偏函数与柯里函数区别

// 偏函数:传入某些固定参数后,返回包含固定参数,并可传入其他参数的函数

// 柯里函数:将n元参数的函数改变为n次函数,每次都只能传入一个参数。

// 访问属性函数

var shallowProperty = function(key) {
    return function(obj) {
        return obj == null ? void 0 : obj[key];
    };
};

//判断是否存在自有属性path

var has = function(obj, path) {
    return obj != null && hasOwnProperty.call(obj, path);
}

//访问对象的深度多级属性 函数

var deepGet = function(obj, path) {
    var length = path.length;
    for (var i = 0; i < length; i++) {
        if (obj == null) return void 0;
        obj = obj[path[i]];
    }
    return length ? obj : void 0;
};

// Helper for collection methods to determine whether a collection

// should be iterated as an array or as an object.

// Related: http://people.mozilla.org/~jo...

// Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094

// js 最大的精确整数值

var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = shallowProperty('length');
var isArrayLike = function(collection) {
    var length = getLength(collection);
    return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};

// Collection Functions

// --------------------

// The cornerstone, an each implementation, aka forEach .

// Handles raw objects in addition to array-likes. Treats all

// sparse array-likes as if they were dense.

// each与map区别

// each中iteratee函数执行结果不保存,返回obj本身

// map中iteratee函数执行结果保存到数组results中,并返回。

_.each = _.forEach = function(obj, iteratee, context) {
    iteratee = optimizeCb(iteratee, context);
    var i,
        keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length;
    for (i = 0; i < length; i++) {
        var currentKey = keys ? keys[i] : i;
        iteratee(obj[currentKey], currentKey, obj);
    }
    return obj;
};

// Return the results of applying the iteratee to each element.

_.map = _.collect = function(obj, iteratee, context) {
    //包装执行函数上下文
    iteratee = cb(iteratee, context);
    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;
        results[index] = iteratee(obj[currentKey], currentKey, obj);
    }
    return results;
};

// Create a reducing function iterating left or right.

// 创建迭代函数,可选遍历方向

var createReduce = function(dir) {
    // Wrap code that reassigns argument variables in a separate function than
    // the one that accesses `arguments.length` to avoid a perf hit. (#1991)
    // perf hit是performance hit的简写。
    // arguments的使用,会导致一些性能问题。如chrome nodejs的v8引擎在使用arguments会代码优化步骤被跳过。
    var reducer = function(obj, iteratee, memo, initial) { //func2
        var keys = !isArrayLike(obj) && _.keys(obj),
            length = (keys || obj).length,
            index = dir > 0 ? 0 : length - 1;
        if (!initial) {
            memo = obj[keys ? keys[index] : index];
            index += dir;
        }
        for (; index >= 0 && index < length; index += dir) {
            var currentKey = keys ? keys[index] : index;
            memo = iteratee(memo, obj[currentKey], currentKey, obj);
        }
        return memo;
    };
    // 此处返回func1函数,作用域中包含initial变量,存放了arguments.length,
    // 用意就在于将arguments变量从reducer函数中分离出来,
    // 并在调用reducer函数时,释放argumenrs变量,避免性能问题。
    return function(obj, iteratee, memo, context) { //func1
        var initial = arguments.length >= 3;
        return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
    };
};

// 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);

// Return the first value which passes a truth test. Aliased as detect .

// 返回第一个断言问真的值

_.find = _.detect = function(obj, predicate, context) {
    var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey;
    var key = keyFinder(obj, predicate, context);
    if (key !== void 0 && key !== -1) return obj[key];
};

// 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) {
        if (predicate(value, index, list)) results.push(value);
    });
    return results;
};

// Return all the elements for which a truth test fails.

// 返回所有断言为假的值,

// 内部直接调用filter

_.reject = function(obj, predicate, context) {
    return _.filter(obj, _.negate(cb(predicate)), context);
};

// 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),
        length = (keys || obj).length;
    for (var index = 0; index < length; index++) {
        var currentKey = keys ? keys[index] : index;
        if (!predicate(obj[currentKey], currentKey, obj)) return false;
    }
    return true;
};

// 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;
};

// Determine if the array or object contains a given item (using === ).

// Aliased as includes and include .

// 判断从fromIndex索引开始,是否包含item

_.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
    if (!isArrayLike(obj)) obj = _.values(obj);
    if (typeof fromIndex != 'number' || guard) fromIndex = 0;
    return _.indexOf(obj, item, fromIndex) >= 0;
};

// Convenience version of a common use case of map : fetching a property.

// 萃取数组对象中某属性值,返回一个数组

_.pluck = function(obj, key) {
    return _.map(obj, _.property(key));
};

// Convenience version of a common use case of filter : selecting only objects

// containing specific key:value pairs.

// 遍历list中的每一个值,返回一个数组,这个数组里的元素包含 properties 所列出的键 - 值对。

_.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.

// 遍历整个list,返回 matches(匹配) properties参数所列出的所有 键 - 值 对的第一个值。

_.findWhere = function(obj, attrs) {
    return _.find(obj, _.matcher(attrs));
};

// Return the maximum element (or element-based computation).

// 返回list中的最大值。如果传递iteratee参数,iteratee将作为list中每个值的 排序 依据。

// 如果list为空,将返回-Infinity,所以你可能需要事先用isEmpty检查 list 。

_.max = 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;
};

// Return the minimum element (or element-based computation).

// 返回list中的最小值。如果传递iteratee参数,iteratee将作为list中每个值的排序依据。

// 如果list为空,将返回Infinity,所以你可能需要事先用isEmpty检查 list 。

_.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 a collection.

_.shuffle = function(obj) {
    return _.sample(obj, Infinity);
};

// Sample n random values from a collection using the modern version of the

// Fisher-Yates shuffle .

// If n is not specified, returns a single random element.

// The internal guard argument allows it to work with map .

// 从 list中产生一个随机样本。传递一个数字表示从list中返回n个随机元素。否则将返回一个单一的随机项。

_.sample = function(obj, n, guard) {
    if (n == null || guard) {
        if (!isArrayLike(obj)) obj = _.values(obj);
        return obj[_.random(obj.length - 1)];
    }
    var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
    var length = getLength(sample);
    //取一个符合0-length的合理值
    n = Math.max(Math.min(n, length), 0);
    var last = length - 1;
    for (var index = 0; index < n; index++) {
        var rand = _.random(index, last);
        var temp = sample[index];
        sample[index] = sample[rand];
        sample[rand] = temp;
    }
    return sample.slice(0, n);
};

// Sort the object's values by a criterion produced by an iteratee.

// 返回一个(稳定的)排序后的list拷贝副本。

// 如果传递iteratee参数,iteratee将作为list中每个值的排序依据。

// 用来进行排序迭代器也可以是属性名称的字符串(比如 length)。

_.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;
        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');
};

// An internal function used for aggregate "group by" operations.

// 对集合每个元素调用iteratee函数后,得到key。

// 然后再根据behavior进行分组

var group = function(behavior, partition) {
    return function(obj, iteratee, context) {
        var result = partition ? [
            [],
            []
        ] : {};
        iteratee = cb(iteratee, context);
        _.each(obj, function(value, index) {
            var key = iteratee(value, index, obj);
            behavior(result, value, key);
        });
        return result;
    };
};

// Groups the object's values by a criterion. Pass either a string attribute

// to group by, or a function that returns the criterion.

// 把一个集合分组为多个集合,通过 iterator 返回的结果进行分组.

// 如果 iterator 是一个字符串而不是函数, 那么将使用 iterator 作为各元素的属性名来对比进行分组。

_.groupBy = group(function(result, value, key) {
    if (has(result, key)) result[key].push(value);
    else result[key] = [value];
});

// Indexes the object's values by a criterion, similar to groupBy , but for

// when you know that your index values will be unique.

// 给定一个list,和 一个用来返回一个在列表中的每个元素键 的iterator 函数(或属性名), 返回一个每一项索引的对象。

// 和groupBy非常像,但是当你知道你的键是唯一的时候可以使用indexBy 。

_.indexBy = group(function(result, value, key) {
    result[key] = value;
});

// 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.

// 排序一个列表组成多个组,并且返回各组中的对象的数量的计数。

// 类似groupBy,但是不是返回列表的值,而是返回在该组中值的数目。

_.countBy = group(function(result, value, key) {
    if (has(result, key)) result[key]++;
    else result[key] = 1;
});

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

// Safely create a real, live array from anything iterable.

_.toArray = function(obj) {
    if (!obj) return [];
    if (_.isArray(obj)) return slice.call(obj);
    if (_.isString(obj)) {
        // Keep surrogate pair characters together
        return obj.match(reStrSymbol);
    }
    if (isArrayLike(obj)) return _.map(obj, _.identity);
    return _.values(obj);
};

// Return the number of elements in an object.

_.size = function(obj) {
    if (obj == null) return 0;
    return isArrayLike(obj) ? obj.length : _.keys(obj).length;
};

// Split a collection into two arrays: one whose elements all satisfy the given

// predicate, and one whose elements all do not satisfy the predicate.

// 将 list 拆分为两个数组:第一个数组其元素都满足predicate迭代函数, 而第二个的所有元素均不能满足predicate迭代函数。

// predicate 通过 iteratee 进行转换,以简化速记语法。

_.partition = group(function(result, value, pass) {
    result[pass ? 0 : 1].push(value);
}, true);

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Python Cookbook 中文版,第 3 版

Python Cookbook 中文版,第 3 版

David M. Beazley、Brian K. Jones / 陈舸 / 人民邮电出版社 / 2015-5-1 / 108.00元

《Python Cookbook(第3版)中文版》介绍了Python应用在各个领域中的一些使用技巧和方法,其主题涵盖了数据结构和算法,字符串和文本,数字、日期和时间,迭代器和生成器,文件和I/O,数据编码与处理,函数,类与对象,元编程,模块和包,网络和Web编程,并发,实用脚本和系统管理,测试、调试以及异常,C语言扩展等。 本书覆盖了Python应用中的很多常见问题,并提出了通用的解决方案。......一起来看看 《Python Cookbook 中文版,第 3 版》 这本书的介绍吧!

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具