JavaScript:事件处理程序:在哪里声明变量 - 本地或闭包(vs开销)?

pet*_*ete 4 javascript jquery closures javascript-events

我发现自己编写了包含事件处理程序的各种函数.感觉最好在父函数(闭包)的根处声明处理函数所需的变量,特别是如果它们是jQuery选择,多个处理程序所需的常量,或者我不想要的一些预计算每次激活事件时重复.一个简单的例子:

var touchDrag = function() {

    var x, y, i;
    var $mySelection = $('.selection');

    $('#some-elem').on( 'touchmove', function(e) {

        x = something;
        y = something;
        i++;
        $mySelection.doSomething();

        // more code..
    });
}  
Run Code Online (Sandbox Code Playgroud)

但是,我经常看到在处理函数(本地)内声明的处理程序变量.问了几位程序员,随后进行了一些辩论,但没有一个明确的答案.

我理解保持变量范围尽可能小是一种好习惯.但是,对于像.scroll()or 这样经常触发的事件touchmove,在我看来,每次触发事件时都会有一个很大的开销重新声明它们(而不是只分配一次每个var)?

jfr*_*d00 6

通常,变量应该在需要它的最小范围内定义.因此,如果在touchmove事件的实际处理过程中只需要一个变量,并且它不会将状态从一个事件传递到下一个事件,那么我通常会在实际的touchmove事件处理程序中声明它,因此它的范围尽可能小,并且不使用时会收集垃圾.

当然,有一些例外情况可能会从更高的范围内宣布,例如:

  1. 预计算. 而不是每次需要时计算某些东西,而是计算一次并保持方便.如果这些是用户触发的事件,那么小的预先计算的执行实际上很少与用户时间相关.

  2. 保持从一次事件发生到下一次事件的状态.这需要将变量声明为更高级别,以便它可以从一个事件持续到下一个事件.

  3. 与其他代码共享. 如果变量中的值由同一上下文中的其他处理程序共享,那么显然您必须将其声明为足够高的级别,以便所有想要访问它的人都可以使用它.一个常见的例子可能是一个事件想要启动而另一个事件可能想要停止的计时器.

以下是避免在更高范围内声明变量的一些原因,这使得它们的生命周期更长:

  1. 内存泄漏. 您可能会无意中创建一些内存泄漏.如果你正在缓存一个DOM元素,然后你的代码中的其他地方决定替换那个DOM元素,那么现在你在JS代码中引用了一个DOM元素,这样就不会让它被垃圾收集.

  2. 陈旧的价值观. 上面的相同方案可能导致缓存变量中的值错误.如果您根据需要进行提取或计算而不是长时间保存该值,则始终存在较低的陈旧值风险.

  3. 代码清晰度和稳健性. 如果你有一组在更高范围内声明的变量,然后是一堆函数,每个函数都使用其中的一些变量,那么使用什么不是很清楚.如果它们实际上是可以相互调用的函数,那么一个函数可以使用各种函数来破坏另一个函数使用的变量.从它的逻辑极端来看,这正是局部变量优于全局变量的原因.虽然一级更高的范围没有全局变量那么糟糕,但它仍然存在一些相同的问题.


此外,响应您的一个问题,声明和初始化局部变量不是"大开销",除非计算初始值的工作是一项耗时的任务.但是在你的例子中,简单地移动var x到使用它的函数中,当函数启动时性能不会明显下降,事实上,它甚至可能在函数执行期间提高性能,因为访问局部变量是比访问更高范围的变量更快(在检查更高的范围之前,首先检查本地命名空间的变量).

至于$mySelection你现在添加到你的问题的变量,我会在需要它的最小范围内声明它,直到/除非你有任何数据/信息,它的初始化性能实际上导致你出问题.通常,简单的选择器搜索操作在现代CPU上非常快.

与几乎所有性能问题一样 - 在您有任何证据表明它实际上是一个问题之前过早地尝试解决性能问题很少有效地利用时间或理由偏离最佳编码实践.如有疑问,请尽可能保持代码简单且独立.