each

_.each(list, iteratee, [context])Alias: forEach
遍历 list 中的所有元素,按顺序用每个元素当做参数调用 iteratee 函数。如果传递了 context 参数,则把 iteratee 绑定到 context 对象上。每次调用 iteratee 都会传递三个参数:(element, index, list)。如果 list 是个 JavaScript 对象,iteratee 的参数是 (value, key, list))。返回 list 以方便链式调用。

1
2
3
4
_.each([1, 2, 3], alert);
=> alerts each number in turn...
_.each({one: 1, two: 2, three: 3}, alert);
=> alerts each number value in turn..

注意:集合函数能在数组,对象,和类数组对象,比如 arguments, NodeList 和类似的数据类型上正常工作。 但是它通过鸭子类型工作,所以要避免传递带有一个数值类型 length 属性的对象。每个循环不能被破坏 - 打破, 使用_.find* 代替,这也是很好的注意。*

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import optimizeCb from './_optimizeCb.js';
import isArrayLike from './_isArrayLike.js';
import keys from './keys.js';

export default function each(obj, iteratee, context) {
// 生成对应函数的迭代器
iteratee = optimizeCb(iteratee, context);
var i, length;
// 是数组或类数组
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
// 是 object
var _keys = keys(obj);
for (i = 0, length = _keys.length; i < length; i++) {
iteratee(obj[_keys[i]], _keys[i], obj);
}
}
return obj;
}

辅助函数

optimizeCb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 返回一个当前引擎版本有效的内部函数
export default function optimizeCb(func, context, argCount) {
// 上下文为空,直接返回原函数
if (context === void 0) return func;
// 如果没有传递参数个数,默认为 3 个
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
return func.call(context, value);
};
// 省略了使用两个参数的情况,因为没有用到.
case 3: return function(value, index, collection) {
// this 指向 context,传入当前值,索引,以及集合
return func.call(context, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
}

IsArrayLike

_isArrayLike.js

1
2
3
4
5
import createSizePropertyCheck from './_createSizePropertyCheck.js';
import getLength from './_getLength.js';

// 确定是否为一个集合(数组或类数组)
export default createSizePropertyCheck(getLength);

_createSizePropertyCheck.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// js 可以精确表示的最大整数
import { MAX_ARRAY_INDEX } from './_setup.js';

// `isArrayLike` 和 `isBufferLike` 的 内部逻辑
// 传入获取长度的方法
export default function createSizePropertyCheck(getSizeProperty) {
// 返回一个函数,传入一个集合
return function(collection) {
// 对集合取长度
var sizeProperty = getSizeProperty(collection);
// 如果长度是 number 同时在有效范围内,表示是一个合法的集合(数组或类数组)
return typeof sizeProperty == 'number' && sizeProperty >= 0 && sizeProperty <= MAX_ARRAY_INDEX;
}
}

_getLength.js

1
2
3
4
import shallowProperty from './_shallowProperty.js';

// 获取对象 `length` 属性的 内部方法.
export default shallowProperty('length');

_shadowProperty.js

1
2
3
4
5
6
// 生成一个从 obj 中获取 对应key 的内部函数.
export default function shallowProperty(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
}

keys

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import isObject from './isObject.js';
import { nativeKeys, hasEnumBug } from './_setup.js';
import has from './_has.js';
import collectNonEnumProps from './_collectNonEnumProps.js';

// Retrieve the names of an object's own properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`.
export default function keys(obj) {
// 不是对象抛出空数组
if (!isObject(obj)) return [];
// 存在 keys 方法 返回 Object.keys 结果
if (nativeKeys) return nativeKeys(obj);

// 以上条件都不满足,使用 for in 遍历
var keys = [];
for (var key in obj) if (has(obj, key)) keys.push(key);
// 是否小于 IE9.
if (hasEnumBug) collectNonEnumProps(obj, keys);
return keys;
}