JavaScript闭包如何处理变量?

3
假设我们有一个名为containerFunction的容器函数,并在其中定义了两个函数:
var innerFunction1link;
var innerFunction2link;

function containerFunction() {
    var someInnerVariable = 1;

    innerFunction1link = innerFunction1;
    innerFunction2link = innerFunction2;

    function innerFunction1() {
        console.log("I'm 1 " + someOuterVariable + " " + (someInnerVariable++));
    }

    function innerFunction2() {
        console.log("I'm 2 " + someOuterVariable + " " + (someInnerVariable++));
    }
}


containerFunction();
var someOuterVariable = 42;

// Point A
innerFunction1link();
innerFunction2link();

someOuterVariable = "WAT?!";

// Point B
innerFunction1link();
innerFunction2link();

现在是问题时间。根据《JavaScript 忍者秘籍》一书,每个闭包都会有自己的私有变量集(包括稍后定义的变量,比如someOuterVariable):

private bubbles

因此,在containerFunction之外引用innerFunction1innerFunction2时,它们被称为innerFunction1linkinnerFunction2link,将具有完整的“私有气泡”及其中的所有变量。在“A点”,两个函数都将正常执行,并输出:
I'm 1 42 1
I'm 2 42 2

接着,当我将someOuterVariable设置为"WAT?!"时,输出结果为:

I'm 1 WAT?! 3
I'm 2 WAT?! 4

在“私有空间”中,someOuterVariablesomeInnerValue的值将如何更新(如果有1000个这样的“气泡”,会怎样)?它是否实际上跟踪所有变量的所有引用?如果一个闭包更新了someOuterVariable,那么它的值将如何传播到其他闭包?
更新:即使innerFunction在不同的上下文中定义,它们仍然会“共享”变量。
function containerFunction1() {
    var someInnerVariable = 1;

    innerFunction1link = innerFunction1;

    function innerFunction1() {
        console.log("I'm 1 " + someOuterVariable + " " + (someInnerVariable++));
    }
}

function containerFunction2() {
    var someInnerVariable = 1;

    innerFunction2link = innerFunction2;
    function innerFunction2() {
        console.log("I'm 2 " + someOuterVariable + " " + (someInnerVariable++));
    }
}

containerFunction1();
containerFunction2();
var someOuterVariable = 42;

innerFunction1link();
innerFunction2link();

someOuterVariable = "WAT?!";

innerFunction1link();
innerFunction2link();

输出:

I'm 1 42 1 
I'm 2 42 1
I'm 1 WAT?! 2
I'm 2 WAT?! 2 
2个回答

6
比使用私有泡泡更好的描述是,每个函数都保留对其定义所在作用域的引用。必须明确它是一个引用而不是副本。这解释了为什么两个内部函数指向相同的作用域,因此相同的变量集也可以在外部作用域或通过任何指向该作用域的函数中进行更改。补充说明:在JavaScript中,每次调用函数时都会创建一个作用域(还有其他作用域,特别是全局作用域,但也可以使用with或try/catch创建)。

好的,那么如果内部函数将在不同的容器中创建呢?它们不应该有不同的上下文吗?(我已经把这个问题添加到了里面) - mishik
我所指的“…”是未更改的部分。已添加。 - mishik
我所说的“保持引用”的意思是它是一个引用,而不是外部作用域的副本。这就是为什么在日志中反映了someOuterVariable的更改。 - Denys Séguret
我这么说是因为通过更新 someOuterVariable,我同时更新了内部函数。但现在我明白为什么它能够工作了。特别感谢 Bergi 的 看透你的泡沫墙 - mishik

1

因此,在 containerFunction 外部引用 innerFunction1innerFunction2 作为 innerFunction1linkinnerFunction2link,它们将在其“私有作用域”中具有完整的变量集。

实际上不是这样的。它们的作用域相当空洞,但从其中你可以看到你的作用域的墙壁里面的东西,以及更大的 containerFunction 的作用域。在那里,你可以看到 4 个变量:containerFunctionsomeInnerVariableinnerFunction1innerFunction2。而从该作用域中,你可以看到全局作用域,包括 innerFunction1linkinnerFunction2linksomeVariablecontainerFunctionsomeOuterVariable 变量。

因此,与其在每个作用域中复制每个变量,它们只是指向更高层次作用域中共享变量的指针。如果它们从任何地方更新,你也可以从私有作用域中看到它们被更新了。

现在用“作用域”替换每个单词“bubble” :-)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接