setTimeout 是否创建函数实例?

Does setTimeout create function instances?

这可能是一个菜鸟问题。下面的代码就像我想要的那样工作,但我不知道为什么。

var x = 0

init_page = function(){
    x++;


    var y = x;

    setTimeout(go, 1000);
    function go(){
        $('body').append('<div>Y: '+y+'</div>');
    }

}
init_page();

Demo

如果您快速单击 link 几次,它将打印 1,2,3,4...

我的问题是它们都调用了同一个函数,但它的作用就好像函数是这样实例化的:

    function go1(){
        $('body').append('<div>Y: 1</div>');
    }
    function go2(){
        $('body').append('<div>Y: 2</div>');
    }
    function go3(){
        $('body').append('<div>Y: 3</div>');
    }

不应该都是执行时打印相同的数字(最大的数字)而不是点击时的数字吗? 我希望看到 4、4、4、4,因为 go() 对于所有间隔都是相同的函数。

由于 go 包含在 init_page() 中,每次您排队一个新的队列时,它都会获得定义的函数的不同实例。其中每一个都有自己的 y 变量,如 init_page 中所声明的那样,该变量将具有在 [=13] 时从 x 的当前值分配给它的值=] 被称为 - 这就是为什么您会看到自己的行为。

您正在声明一个局部变量 y,它与您的内联函数 go 的作用域捆绑在一起。变量 y 被初始化为 x 的值 - 但由于它是一个原始值(按值传递,而不是引用)更改为 yx 不会影响每个其他.

如果您将 y 移出 init_page,此行为将会改变。

这种技术在 Javascript 中称为 "Closure"。当内部函数可以访问外部函数的范围并为特定调用保留外部范围时。当 "setTimeout" 执行 "go" 函数时,这实际上发生在您的代码中 - 它会在每次调用时保留 "init_page" 函数的外部范围状态。

闭包是 javascript 中的强大功能之一,您可以查看以下使用它们可以实现的示例:

  1. 封装 - 使您的函数成员私有(只能在您的函数(对象)范围内访问)。
  2. 在循环内处理异步调用时保留外部作用域状态。 (在你的情况下 setTimeout)

在这里你也可以找到一些详细的答案:How do JavaScript closures work?