本篇继续分析下 for
家族的方法,for
方法的主要目的是实现对数组、类数组对象和普通对象的迭代。包括forEach
、forEachRight
、forOwn
、forOwnRight
及依赖的基础方法。
对应源码分析已推到 github
仓库: https://github.com/MageeLin/lodash-source-code-analysis
依赖路径图
for
家族方法的依赖路径图如下所示:

从路径图可以发现,forOwn
和 forOwnRight
方法没有依赖其他方法。而 forEach
却依赖了几个内部方法 arrayEach
、baseEach
、baseForOwn
和 baseFor
。同理 forEachRight
也是相似的方式。
forOwn
按照命名来看,其实是有 baseForOwn
这个基础方法的,但是 forOwn
并没有引用它,而是只用一个文件来实现。怀疑是之前用过但后来优化成了 Object.keys
。
forOwn
和 forOwnRight
都是分成 2
步实现:
Object.keys(object)
取到所有的对象自有可迭代属性键。
- 按照正正序或者反序迭代,同时调用
iteratee
。
注意: iteratee
中返回 false
并不能打断迭代。
forOwn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
function forOwn(object, iteratee) { object = Object(object); Object.keys(object).forEach((key) => iteratee(object[key], key, object)); }
export default forOwn;
|
forOwnRight
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
function forOwnRight(object, iteratee) { if (object == null) { return; } const props = Object.keys(object); let length = props.length; while (length--) { iteratee(object[props[length]], iteratee, object); } }
export default forOwnRight;
|
forEach
forEach
实现时用了不同的迭代方法进行优化,分为下面三种情况:
Array.isArray(collection)
来判断是否是数组类型,是的话就执行 arrayEach
;
isArrayLike(collection)
来判断是不是类数组对象,是的话就 baseEach
直接迭代;
- 以上两种情况都不是,就当成普通对象,用
baseFor
来迭代;
forEachRight
的实现与 forEach
基本相同,只不过从后往前迭代而已。
forEach
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import arrayEach from './.internal/arrayEach.js'; import baseEach from './.internal/baseEach.js';
function forEach(collection, iteratee) { const func = Array.isArray(collection) ? arrayEach : baseEach; return func(collection, iteratee); }
export default forEach;
|
arrayEach
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
function arrayEach(array, iteratee) { let index = -1; const length = array.length;
while (++index < length) { if (iteratee(array[index], index, array) === false) { break; } } return array; }
export default arrayEach;
|
baseEach
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import baseForOwn from './baseForOwn.js'; import isArrayLike from '../isArrayLike.js';
function baseEach(collection, iteratee) { if (collection == null) { return collection; } if (!isArrayLike(collection)) { return baseForOwn(collection, iteratee); } const length = collection.length; const iterable = Object(collection); let index = -1;
while (++index < length) { if (iteratee(iterable[index], index, iterable) === false) { break; } } return collection; }
export default baseEach;
|
baseForOwn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import baseFor from './baseFor.js'; import keys from '../keys.js';
function baseForOwn(object, iteratee) { return object && baseFor(object, iteratee, keys); }
export default baseForOwn;
|
baseFor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
function baseFor(object, iteratee, keysFunc) { const iterable = Object(object); const props = keysFunc(object); let { length } = props; let index = -1;
while (length--) { const key = props[++index]; if (iteratee(iterable[key], key, iterable) === false) { break; } } return object; }
export default baseFor;
|
思考
ECMA262 明确规定了,Array
、Set
和 Map
的实例是可迭代对象,但是 Object
的实例不是,所以原生的 forEach
是无法应用在普通的对象上的,lodash 在这里把 Object
做了兼容。
原生想迭代一个对象的所有属性可以如下几种:
for...in
Object.keys(o)
Object.values(o)
Object.entries(o)
Object.getOwnPropertyNames(o)
前端记事本,不定期更新,欢迎关注!