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

时间:2014-02-28 18:37:19

标签: javascript jquery javascript-events closures

我发现自己编写了包含事件处理程序的各种函数。感觉最好在父函数(闭包)的根处声明处理函数所需的变量,特别是如果它们是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..
    });
}  

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

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

2 个答案:

答案 0 :(得分:6)

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

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

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

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

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

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

    1. 内存泄漏。您可能会无意中造成内存泄漏。如果你正在缓存一个DOM元素,然后你的代码中的其他地方你或其他人决定替换那个DOM元素,那么现在你在JS代码中引用了那个DOM元素,以防止它被垃圾收集。

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

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


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

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

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

答案 1 :(得分:1)

尽管重用尽可能多的代码是件好事,但在某些情况下,重用变量可能会导致意外问题

  • 共享变量可以通过副作用进行更改。例如,在一些存在大量事件的大型应用程序中,使用它们之间的共享变量可能会导致一些意外行为,因为事件是异步的,没有人可以预测事件何时完成

  • (在我看来)使用共享变量很难读取和调试,因为我不知道是否有其他东西在使用它(这会在重构我的代码时导致一些问题)