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

JavaScript:事件处理程序:在哪里声明变量-本地或闭包(与开销相比)?,javascript,jquery,closures,jquery-events,Javascript,Jquery,Closures,Jquery Events,我发现自己正在编写各种包含事件处理程序的函数。最好在父函数(闭包)的根处声明处理函数所需的变量,特别是如果它们是jQuery选择、多个处理函数所需的常量,或者我不想在每次触发en事件时重复的一些预计算。一个简单的例子: var touchDrag = function() { var x, y, i; var $mySelection = $('.selection'); $('#some-elem').on( 'touchmove', function(e) {

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

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..
    });
}  
但是,我经常看到在处理函数(local)中声明处理变量。问几个编码员,一些争论接踵而至,但没有一个明确的答案


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

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

当然,在更高的范围内声明它可能会有一些例外,例如:

  • 预计算。与其每次需要时都计算,不如只计算一次并放在手边。如果这些是用户触发的事件,那么一个小的预计算的性能实际上很少与用户时间相关

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

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

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

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

  • 过时值。上述相同情况可能导致缓存变量中的值错误。如果您根据需要获取或计算值,而不是长时间保存值,则过时值的风险总是较小

  • 代码的清晰性和健壮性。如果在更高的范围内声明了一组变量,然后声明了一组函数,每个函数都使用了其中的一些变量,那么谁在使用什么就不太清楚了。如果这些函数实际上是可以互相调用的函数,那么一个函数就有各种各样的可能性来击败另一个函数所使用的变量。从逻辑上讲,这就是为什么局部变量比全局变量更可取的原因。虽然更高一级的作用域没有全局变量那么糟糕,但它仍然存在一些相同的问题


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

    至于您现在添加到问题中的
    $mySelection
    变量,我将在需要它的最小范围内声明它,直到/除非您有任何数据/信息表明它的初始化性能实际上给您带来了问题。一般来说,简单的选择器搜索操作非常快现代CPU


    与几乎所有的性能问题一样,在没有任何证据表明某个性能问题实际上是一个问题之前,试图过早地解决该问题很少是一种有效的时间利用,也不是偏离最佳编码实践的理由。如果有疑问,请尽可能保持代码的简单和自包含性。

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

    • 共享变量的更改可能会产生副作用。例如,在一些有大量事件的大型应用程序中,在其中使用共享变量可能会导致一些意外行为,因为事件是异步的,没有人能够预测事件何时完成

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


    感谢您的详细解释。我最关心的问题之一是重新获得相同的页面元素选择,这感觉“昂贵”(而且不必要)对我来说,尤其是在滚动或鼠标/触摸移动期间,但也许不是这样?我编辑我的帖子是为了澄清..@pete-我会在事件处理程序中声明
    $var
    ,直到/除非您有任何数据/信息表明其初始化性能实际上是正确的