为什么在嵌套函数之外声明一个计数器变量会使循环慢5倍?

Lys*_*Lys 11 javascript optimization micro-optimization

我正在寻找一些我正在重新审视的JavaScript遗留代码的微优化,并注意到在大多数频繁调用的for循环中,计数器在全局范围内声明一次,在使用它们的函数之外.我很好奇这是否确实是一个优化,因此我在JavaScript中创建了以下测试用例:

var tmp = 0;

function test(){

    let j = 0;

    function letItBe(){

        for(j = 0; j < 1000; j++){
            tmp = Math.pow(j, 2);
        }
    }

    function letItNotBe(){
        for(let l = 0; l < 1000; l++){
            tmp = Math.pow(l, 2);
        }
    }

    console.time("let it be");
    for(var i =0; i < 10000; i++){

        letItBe();
    }
    console.timeEnd("let it be");


    console.time("let it not be");
    for(var i =0; i < 10000; i++){

        letItNotBe();
    }
    console.timeEnd("let it not be");
}

test();
Run Code Online (Sandbox Code Playgroud)

在Chrome,Firefox和NodeJS 中letItNotBe()运行的速度要快得多letItBe()

铬: 在此输入图像描述

的NodeJS:

在此输入图像描述

var改变let没有区别.

最初我的逻辑是,每次调用一个函数时声明一个新的计数器变量确实比最初声明一个变量然后简单地重置为0要慢.然而,事实证明它是完全相反的并且执行时间不同是非常有用的.

我的简单解释是,当计数器变量被声明为使用它的函数时,JS转换器需要以某种方式引用此变量.并且因为它在父范围内,所以在递增时需要更多的执行来引用它.然而,这只是盲目的猜测.

任何人都可以给出任何有意义的解释,为什么会这样,因为我需要重构代码并给出一个有意义的解释mysefl除了我已经拥有的测试:)谢谢.

kit*_*org 9

我读了一本高性能JavaScript的书,作者在第2章"数据访问" - "管理范围"部分 - "标识符解析性能"部分对此进行了解释.

标识符解析不是免费的,因为事实上没有计算机操作确实没有某种性能开销.执行上下文的作用域链越深,标识符就越存在,读取和写入的访问速度就越慢.因此,局部变量总是在函数内部访问最快,而全局变量通常是最慢的(优化的JavaScript引擎能够在某些情况下调整它).

...

所有浏览器的总体趋势是,作用域链越深,标识符就越存在,读取或写入的速度就越慢.

...

鉴于此信息,建议尽可能使用局部变量来提高浏览器的性能而不优化JavaScript引擎.一个好的经验法则是,如果在函数中多次使用超出范围的值,则始终将超出范围的值存储在局部变量中.

在你的情况下,letItBeletItNotBe以相同的方式工作,使用相同的超出范围的tmp变量,并且它们都是闭包.唯一的区别是for循环的计数器变量:

  • 变量j是为函数定义的test(),它是函数的"超出范围" letItBe(),因此执行letItBe()将导致引擎在标识符解析上做更多的工作
  • 变量lfor循环范围内定义(请参阅for循环中的let关键字),因此分辨率更快