什么是lodash
Lodash 是一个 JavaScript 实用工具库,提供了很多用于处理数据、简化开发等方面的功能。它提供了一组常用的工具函数,用于处理数组、对象、字符串等常见数据结构,同时也包含了一些函数式编程的工具。对于前端开发来说,是个很好用的工具,甚至看过有人说面试不会lodash被嘲讽了。
我所在的公司项目中也用了这个库,今天就听同事吐槽,他循环两万条数据,进入卡住了溢出了,找了半天问题,最后没想到是foreach的过,换成for循环就没问题了。
那么为什么呢,lodash是怎么实现foreach的呢?源码如下:
import arrayEach from './.internal/arrayEach.js'; import baseEach from './.internal/baseEach.js'; /** * Iterates over elements of `collection` and invokes `iteratee` for each element. * The iteratee is invoked with three arguments: (value, index|key, collection). * Iteratee functions may exit iteration early by explicitly returning `false`. * * **Note:** As with other "Collections" methods, objects with a "length" * property are iterated like arrays. To avoid this behavior use `forIn` * or `forOwn` for object iteration. * * @since 0.1.0 * @alias each * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array|Object} Returns `collection`. * @see forEachRight, forIn, forInRight, forOwn, forOwnRight * @example * * forEach([1, 2], value => console.log(value)) * // => Logs `1` then `2`. * * forEach({ 'a': 1, 'b': 2 }, (value, key) => console.log(key)) * // => Logs 'a' then 'b' (iteration order is not guaranteed). */ function forEach(collection, iteratee) { const func = Array.isArray(collection) ? arrayEach : baseEach; return func(collection, iteratee); } export default forEach;
源码感兴趣可以去github上看
以下是一个对 Lodash forEach
源码的简要分析:
function forEach(collection, iteratee) { const func = Array.isArray(collection) ? arrayEach : baseEach; return func(collection, iteratee); } function arrayEach(array, iteratee) { let index = -1; const length = array == null ? 0 : array.length; while (++index < length) { if (iteratee(array[index], index, array) === false) { break; } } return array; } function baseEach(collection, iteratee) { let index = -1; const iterable = Object(collection); const length = iterable.length; while (++index < length) { if (iteratee(iterable[index], index, iterable) === false) { break; } } return collection; }
这里对 Lodash 的 forEach
进行了两个版本的优化:arrayEach
用于数组,baseEach
用于通用集合。它们都使用了一个 while
循环来迭代集合中的元素,调用传入的 iteratee
函数。
主要要点:
- 类型检测: 在
forEach
函数中,通过Array.isArray
来判断集合的类型。如果是数组,则使用arrayEach
进行迭代;否则,使用baseEach
。 - 迭代逻辑:
arrayEach
和baseEach
都使用了一个while
循环来遍历集合。在每次迭代中,调用传入的iteratee
函数,并检查其返回值。如果返回值为false
,则中断循环。 - 性能优化: Lodash 的实现对性能进行了一些优化,比如使用了
length
变量缓存集合的长度,减少了重复计算。 - 处理对象: 在
baseEach
中,通过Object(collection)
将集合转换为一个可迭代的对象。这样可以确保baseEach
对于对象的处理更加一致,无论是否为数组。
那么为什么在特别大数据下会出现问题呢
使用 forEach
处理几万条数据量可能会导致栈溢出的问题,这通常是因为递归调用造成的。Lodash 的 forEach
实现是基于递归的,而不是基于循环的,因此对于大型数据集,递归深度可能会导致栈溢出。
为了解决这个问题,可以考虑使用其他方法,比如使用普通的 for
循环等。