Array.forEach() and closure



我阅读了有关JavaScript优化的文章后,我意识到需要删除代码中的封闭以优化内存使用。

我的一种代码模式是尽可能多地使用Array.forEach(),即使在这种情况下也是如此:

  • 使用数组中的项目修改外部对象

    function updateSomething(array, toChange) {
      array.forEach(item => {
        toChange[item] = ''; // do something to change the object
      });
    }
    
  • 创建嵌套Array.forEach()

    array1.forEach(item1 => {
      array2.forEach(item2 => {
        doSomething(item1, item2);
      });
    });
    

显然,Array.forEach()中使用的回调函数会创建关闭。那么,在这些情况下,我是否滥用Array.forEach()?我应该回到对性能敏感项目中的for循环吗?


跟进

我在for循环和Array.forEach()功能上进行了一些实验,并使用节点v7.6.0进行了一些实验。我没有关于绩效测试的事先经验。所以让我知道我是否做错了。

  • Array.forEach()访问外部变量

    // get the baseline of memory usage
    gc();
    let baseline = process.memoryUsage();
    console.log(`Baseline memory usage: ${baseline.heapUsed / 1024 } KB`);
    let data = { test: 0 };
    function test(data) {
      let array1 = new Array(1000000).fill(1);
      array1.forEach((item) => {
        data.test = data.test + item;
      });
    }
    test(data);
    let final = process.memoryUsage();
    console.log(`Final memory usage: ${final.heapUsed / 1024} KB`);
    console.log(`Memory used: ${(final.heapUsed - baseline.heapUsed) / 1024} KB`);
    

    结果

    Baseline memory usage: 2747.671875 KB
    Final memory usage: 11027.34375 KB
    Memory used: 8279.671875 KB
    
  • for循环

    // get the baseline of memory usage
    gc();
    let baseline = process.memoryUsage();
    console.log(`Baseline memory usage: ${baseline.heapUsed / 1024 } KB`);
    let data = { test: 0 };
    function test(data) {
      let array1 = new Array(1000000).fill(1);
      for(let i = 0; i < 1000000; i++) {
        data.test = data.test + array1[i];
      }
    }
    test(data);
    let final = process.memoryUsage();
    console.log(`Final memory usage: ${final.heapUsed / 1024} KB`);
    console.log(`Memory used: ${(final.heapUsed - baseline.heapUsed) / 1024} KB`);
    

    结果

    Baseline memory usage: 2747.453125 KB
    Final memory usage: 11031.546875 KB
    Memory used: 8284.09375 KB
    
  • 嵌套Array.forEach()

    // get the baseline of memory usage
    gc();
    let baseline = process.memoryUsage();
    console.log(`Baseline memory usage: ${baseline.heapUsed / 1024 } KB`);
    let array1 = new Array(1000).fill(1);
    let array2 = new Array(1000).fill(2);
    array1.forEach((item, index) => {
      array2.forEach(item2 => {
        array1[index] = array1[index] = item2;
      })
    });
    let final = process.memoryUsage();
    console.log(`Final memory usage: ${final.heapUsed / 1024} KB`);
    console.log(`Memory used: ${(final.heapUsed - baseline.heapUsed) / 1024} KB`);
    

    结果

    Baseline memory usage: 2748.109375 KB
    Final memory usage: 3368.5859375 KB
    Memory used: 620.4765625 KB
    
  • 嵌套for循环

    // get the baseline of memory usage
    gc();
    let baseline = process.memoryUsage();
    console.log(`Baseline memory usage: ${baseline.heapUsed / 1024 } KB`);
    let array1 = new Array(1000).fill(1);
    let array2 = new Array(1000).fill(2);
    for (let i = 0; i < 1000; i++) {
      for (let j = 0; j < 1000; j++) {
        array1[i] = array1[i] + array2[j];
      }
    }
    let final = process.memoryUsage();
    console.log(`Final memory usage: ${final.heapUsed / 1024} KB`);
    console.log(`Memory used: ${(final.heapUsed - baseline.heapUsed) / 1024} KB`);
    

    结果

    Baseline memory usage: 2745.59375 KB
    Final memory usage: 3234.2890625 KB
    Memory used: 488.6953125 KB
    

结论

测试#1和#2的存储器使用率增加的差异小于0.1%。因此,这表明Array.forEach()确实具有与传统for环路相同的内存效率,即使它正在访问外部变量,并且似乎会创建封闭。魔术是在内部完成的。

注意到,在测试#3中,array2.forEach()的回调功能初始化了1000次。这可以解释为什么测试#3比测试#4使用更多的内存。

我意识到需要删除代码中的封闭以优化内存使用。

用于内存使用,只有您 store 的关闭。当您遇到内存问题时,您应该检查是否有很多实例的类,每个实例都有自己的封闭实例。这并不意味着您通常应该避免关闭。

我的一个代码模式是尽可能多地使用 Array.forEach()

不要。鉴于您正在使用ES6,因此您应该尽可能多地使用for … of(对于命令循环)。

显然Array.forEach()中使用的回调函数创建关闭

是的,但是在示例中,您表明它们无法避免(无法将其移至静态功能)。鉴于它们只能持续到forEach呼叫,并且将立即被垃圾收集,也没有内存应变。

但是,正如您链接的文章所解释的那样,封闭仍然是昂贵的(与完全不创建它们相比)。

我应该回到绩效敏感项目中的for循环吗?

是的,肯定(至少在 对性能敏感的位置 - 不会全部项目)。但是,原因不是关闭成本,通常是函数的呼叫开销,forEach无法完全优化。

最新更新