JavaScript 在主机函数 returns 之后的事件中可用的局部范围变量。为什么?

JavaScript local scope variable available in event after host function returns. Why?

所以...我来自c++背景,我对JS的了解不多。例如:

function a()
{
   var x = 0; // Local variable

   $(document).on('mousedown', function(){console.log('down: ', x++);});
   $(document).on('mouseup', function(){console.log('up: ', x++);});
   setInterval(function(){console.log('interval: ', x++);}, 1000);

   //W3schools: 'Local variables are deleted when the function is completed.'
}
a();

为什么控制台输出是这样的? (随机点击)


interval:  0
down:  1
up:  2
interval:  3
down:  4
up:  5
interval:  6
down:  7
interval:  8
up:  9
interval:  10
down:  11
interval:  12
up:  13
interval:  14
down:  15
up:  16
interval:  17
interval:  18

我预计它是“undefined”或 NaN 或 null,因为 x 应该在范围的末尾被删除,但似乎所有三个回调都永远引用它......为什么?它是一种新行为还是自旧 JS 版本以来就这样工作?我找不到答案。

函数可以访问 x,因为您在可访问的范围内为它们提供了该变量。它计数是因为在 javascript 中你没有给出实际值而只是对它的引用,所以你所有的回调仍然引用 a() 函数内的 x 。因为它仍然被引用 javascript 的垃圾收集器不会删除它,因为它认为它仍然是 'in use',它确实是(因为你的回调函数使用它来增加它的每次调用)。

W3schools 的评论不完整。大多数时候,变量会在函数结束时被清理(不一定是立即,而是在下次运行垃圾回收时)。但这只是因为大多数时候不再有对这些变量的引用。

但是,由于 javascript 支持函数作为第一个 class 对象,它也支持闭包。当你有这样一行时:

$(document).on('mousedown', function(){console.log('down: ', x++);});

...您正在创建闭包。该闭包是新函数,加上定义它的词法环境。它有一个对 x 的引用,因此即使 a 完成解析,x 也不会被垃圾回收。稍后调用该函数时,它仍然可以很好地访问闭包变量。

x 仍然有可能被垃圾回收,但前提是所有引用它的闭包也被垃圾回收。在这种情况下,您可以通过注销 mouseup 和 mousedown 事件并取消间隔来实现。