_.map
_.map 类似于 Array.prototype.map
,但是更加健壮和完善,下面是 _.map 的源码
1 | // 简化过,这里仅假设obj是数组 |
map方法除了传入要处理的数组之外,还有两个参数 iteratee 和 context, 类似于 Array.prototype.map
中的其他两个参数,其中iteratee表示处理函数, context表示指定的执行上下文,即 this的值。
然后在源码中,我们看到,我们将iteratee 和 context 传入一个cb函数,然后覆盖掉iteratee函数,然后将这个函数用作最终的处理函数。
实际上,需要这么麻烦吗?这里只是使用了iteratee函数处理每次迭代的值,通过context指定this的值
1 | _.map = function(obj, iteratee, context) { |
这样写,程序并不健壮,万一iteratee传入的不是一个函数呢?比如传入一个null,或者一个对象,字符串、数字。。。
使用上面的方法就会报错,那在underscore中呢?
1 | //使用underscore |
我们会发现,underscore尽然还能根据传入的值的类型不同,实现的效果不同。
- 当iteratee不传时,返回一个相同的数组。
- 当iteratee为一个函数,正常处理
- 当iteratee为一个对象,返回元素是否匹配指定的对象
- 当iteratee为字符串,返回元素对应的属性值的集合。
由此我们可以推测处underscore的cb函数中,有对iteratee值类型的判断,然后根据不同的类型,返回不同iteratee函数。
cb
来看看cb的源码
1 | var cb = function(value, context, argCount) { |
这里又使用到了其他函数,我们一个一个来看下
_.iteratee
1 | _.iteratee = builtinIteratee = function(value, context) { |
因为 .iteratee中 `.iteratee = builtinInteratee,所以在cb中 第一个判断 条件值为false,也就是说
return _.iteratee(value, context)`,正常情况下是不会执行的.
但是如果我们在外部修改了 .iteratee函数,结果便会为true,cb函数直接返回 `.iteratee(value, context)`
这个意思其实是说我们自定义的_.iteratee函数来处理value 和 context。
试想我们并不需要现在 _.map这么强大的功能,我们只希望value是一个函数,就用该函数处理数组元素,如果不是函数,就直接返回当前元素,我们可以这样修改。
1 | _.iteratee = function(value, context) { |
当然更多的情况是自定义对不同的value使用不同的处理函数,值得注意的是,underscore中的许多函数都使用到了cb函数,而cb函数又使用了 _.iteratee函数,如果你修改这个函数,会影响到多个函数,这些函数基本都属于集合函数,具体包括 map, find, filter, reject, every, some, max, min, sortBy, groupBy, indexBy, countBy, sortedIndex, partition, 和 unique
。
_.identity
1 | // Keep the identity function around for default iteratees. |
这也就是为什么当map的第二个参数什么都不传的时候,结果会是一个相同数组的原因。
optimizeCb
当value是一个函数的时候,就传入optinmizeCb函数。if (_.isFunction(value)) return optimizeCb(value, context, argCount);
1 | // Internal function that returns an efficient (for current engines) version |
也许你会好奇,为什么我要对 argCount进行判断呢?就不能直接返回吗?比如:
1 | var optimizeCb = function(func, context) { |
当然没有问题,但是为什么underscore要这样做呢? 其实就是为了避免使用 arguments,提高一点性能而已,如果不是写一个库,其实还真没必要做到这点。
而为什么当参数是3个时候,参数名分别是 value, index, collection, 又为什么没有参数2的情况呢?其实这都是更具underscore函数用到的情况,因为没有函数用到了两个参数,于是就省略了,像map函数就会用到3个参数,就根据这三个参数的名字起了这里的变量名。
_.matcher
if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
这段就是用来处理当map的第二个参数是对象的情况:
1 | // Returns a predicate for checking whether an object has a given set of |
_.property
return _.property(value)
;
1 | // Creates a function that, when passed an object, will traverse that object’s |
原来value还可以传一个数组,用来取深层次的值
1 | var person1 = { |