underscore.js 源码分析(一)
_.each
_.map
_.refuce
_.reduceRight
_.find
_.each
实例
_.each(list, iteratee, [context])
这个方法用于循环遍历, 用于循环 list
对象或者数组, iteratee
是一个遍历函数,接收的参数为遍历之后的结果。 当 list
为数组的时候,传递给 iteratee
函数的参数是 (element, index, list)
, 当 list
为对象的时候, 传递给 iteratee
函数的参数是 [value, key, list]
。对于 context
上下文, 这个决定的是 iteratee
中的 this
的指向值。可选参数, 当 context
忽略的时候 this
指向的是全局变量。_.each
返回值是进行遍历的 list
数组对象。1 | function print(value, index, list) { |
源码分析
在underscore.js
源码中, _.each()
源码为下:1 | _.each = _.forEach = function(obj, iteratee, context) { |
- 分别数组和对象的方法 我平常的时候使用
Object.prootype.toString.call().slice(8, -1)
这种方法来分别数组和纯对象,这里使用了一种方法。
1 | if (length === +length) { |
+length
执行的是隐式类型转换,用于将其他的值转换为数字。经过实验,使用 +
进行类型转换的可能性如下: 1 | +null // 0 |
+length
会被转化为 NaN
。iteratee = optimizeCb(iteratee, context);
optimizaCb
函数
optimizaCb
函数用于绑定上下文: 使用 call
以及 apply
的方法实现的改变函数运行的 this
值的改变 接收三个参数: func
运行的函数 context
运行函数需要进行绑定的上下文 argCount
参数的个数 1 | var optimizeCb = function(func, context, argCount) { |
_.each
源码中的使用这个函数的目的是将 iteratee
的上下文 this
绑定到 context
对象。这里有一个 void 0 这里的 void 0 等同于使用 undefined 不同于使用undefined
的原因在于: 在javascript
中undefined
不是一个保留字。代码如下:
1
2
3
4 >var undefined = 1;
>console.log(undefined); // 1 也是可以的
>使用 void 0 作用是这样的
>
_.map
实例
_.map(list, iteratee, [context])
通过 使用变换函数 iteratee
将list
中的值映射到一个新的数组。1 | function filter(value) { |
1 | _.map = _.collect = function(obj, iteratee, context) { |
源码分析
- 使用
&&
以及||
进行计算
这里对于类型转换,使用的时候要注意出现假值的情况使用
&&
以及 ||
会首先对于 * 第一个* 操作数进行计算,根据判断结果来决定返回哪一个操作数。 1 | a || b |
a || b
好像备用条件。 如果条件 a
不成立, 执行条件 b, 如果成立,执行条件 a
a ? a : b
`a && b` 好像通过条件, 如果 `a` 成立,向下执行,如果不成立,打住,返回 `a` 执行的结果 `a ? b : a`
- 使用
Array(length)
创建的是一个length
长度的数组。
_.reduce
实例
_.reduce(list, iteratee, [memo], context)
通过迭代将 list
中的元素归结为一个值。 memo
表示初始参数。1 | function reduceFn(memo, num) { |
源码分析
源码如下:1 | function reduce(obj, iteratee, memo, context) { |
- 在
obj
可能是对象或者数组的情况下,当obj
是对象的时候,需要产生了一个 使用keys
来进行数组操作 - 在迭代的时候, 当没有
memo
初始值的时候, 将数组或者对象的第一个值作为memo
这里,使用index++
来处理的使用 index++ 的时候,相当于 index = index + 1; 但是直接使用的时候还是原来的 index
1
2
3
4
5>let currentIndex = 0;
>let addIndex = currentIndex++; // 相当于先返回 currentIndex 在进行加一操作
>addIndex // 0
>currentIndex // 1
>
- 迭代的过程发生在使用循环赋值的过程中
1
2
3for (; index < length; index++) {
memo = iteratee(memo, obj[currrentIndex], currentIndex, obj);
}
_.reduceRight
实例
_.reduceRight(list, iteratee, [memo], context)
类似于使用_.reduce
不过不同于使用 _.reduce
的是,这个是从右边向左进行遍历操作;1 | function contact(a, b) { |
源码分析
1 | _.reduceRight = _.foldr = function(obj, iteratee, memo, context) { |
- 使用
while
循环进行判断
1 | while (index-- > 0) { |
_.find
实例
_.find(list, predicate, [context])
遍历 list
值 返回第一个通过 predicate
函数返回真值的数值。1 | let list = [1, 2, 3]; |
源码分析
1 | _.find = _.detect = function(obj, predicate, context) { |