词法 Scope/Closures 和全局函数递归

Lexical Scope/Closures and Global Function Recursion

这既是一个词法范围的例子,也是一个确认我自己理解的问题。首先,考虑以下示例:

Html:

<div id="testtxt"></div>

JS:

function fnTest(currentIdx, endIdx) {
    $('#testtxt').html($('#testtxt').html() + 'Function Called ' + currentIdx + '<br />');
    if (currentIdx < endIdx) {
        setTimeout(function(){ 
            fnTest(currentIdx + 1, endIdx); 
        }, 100);
    }
}

fnTest(1, 10);
fnTest(11, 20);

输出:

Function Called 1
Function Called 11
Function Called 2
Function Called 12
Function Called 3
Function Called 13
Function Called 4
Function Called 14
Function Called 5
Function Called 15
Function Called 6
Function Called 16
Function Called 7
Function Called 17
Function Called 8
Function Called 18
Function Called 9
Function Called 19
Function Called 10
Function Called 20

当我第一次 运行 这个例子时,我担心 fnTest 会有一个全局闭包,因此 currentIdx 和 endIdx 会被 fnTest 的两个调用设置和访问。然而事实并非如此。

请让我知道以下是否是解释它的好方法:

每次调用 fnTest 都会创建一个唯一的对象,其中变量 currentIdx 和 endIdx 会在该调用和该调用中的所有子例程的生命周期内存储(这称为闭包)。 setTimeout 调用从匿名函数创建一个新对象,该对象可以访问 fnTest 闭包,因此可以引用 currentIdx 和 endIdx,此 object/function 将在 100 毫秒延迟后执行。在执行时,匿名函数本身将通过调用 fnTest 创建一个新的 fnTest 闭包。此时匿名函数引用的原始fnTest闭包可能会被处理掉。

请在必要时更正我的技术术语。

基本正确,几点:

Each call to fnTest creates a unique object in which the variables currentIdx and endIdx are stored for the lifetime of that call...

对于那个对象的生命周期,正如 Felix 所说,它被称为 环境。该生命周期就像所有其他对象的生命周期一样:只要某物仍然有对它的引用。特别是,它会在 fnTest 返回后继续(在本例中)。

唯一可以引用这些环境对象的是在其中创建的函数,称为闭包(它们 "close over" 环境)。

...and all sub-routines within that call (this is called a closure)

函数称为闭包,而不是环境。

The setTimeout call creates a new object from the anonymous function

不,您的代码正在创建一个匿名函数并将对该函数的引用传递给 setTimeout

...which has access to the fnTest closure and can therefore reference currentIdx and endIdx

它可以访问创建它的环境

this object/function will execute after a 100ms delay. Upon execution the anonymous function will itself create a new fnTest closure by calling fnTest.

它通过调用 fnTest 创建了一个新环境,是的。

At this point the original fnTest closure referenced by the anonymous function may be disposed of.

由于定时器机制已经释放了它对匿名函数的引用,所以没有任何东西再引用匿名函数,它可以被垃圾回收。因为它是从初始调用到 fnTest 引用环境的唯一事物,所以该环境也可以被垃圾收集。

我们对上面的细节做了一些 的修改,但重要的概念是正确的。