这既不是范围问题,也不是封闭问题。问题在于理解之间声明 and 表达式.
JavaScript 代码,即使是 Netscape 的第一个 JavaScript 版本和 Microsoft 的第一个副本,也是分两个阶段处理的:
第 1 阶段:编译 - 在此阶段,代码被编译成语法树(以及字节码或二进制文件,具体取决于引擎)。
第 2 阶段:执行 - 然后解释解析后的代码。
函数的语法宣言 is:
function name (arguments) {code}
参数当然是可选的(代码也是可选的,但这有什么意义呢?)。
但 JavaScript 还允许您使用以下方式创建函数表达式。函数表达式的语法与函数声明类似,只是它们是在表达式上下文中编写的。表达式为:
- 右侧的任何内容
=
标志(或:
关于对象字面量)。
- 括号内的任何内容
()
.
- 函数的参数(这实际上已经在 2 中涵盖了)。
表达式 unlike 声明在执行阶段而不是编译阶段进行处理。正因为如此,表达式的顺序很重要。
所以,澄清一下:
// 1
(function() {
setTimeout(someFunction, 10);
var someFunction = function() { alert('here1'); };
})();
第一阶段:编译。编译器看到变量someFunction
被定义,所以它创建它。默认情况下,创建的所有变量的值都是未定义的。请注意,编译器此时还无法分配值,因为这些值可能需要解释器执行一些代码来返回要分配的值。在这个阶段我们还没有执行代码。
第二阶段:执行。解释器看到你想要传递变量someFunction
设置超时。确实如此。不幸的是目前的价值someFunction
未定义。
// 2
(function() {
setTimeout(someFunction, 10);
function someFunction() { alert('here2'); }
})();
第一阶段:编译。编译器看到您正在声明一个名为 someFunction 的函数,因此它会创建它。
第 2 阶段:口译员发现您想要通过someFunction
到setTimeout。确实如此。当前值someFunction
是其编译后的函数声明。
// 3
(function() {
setTimeout(function() { someFunction(); }, 10);
var someFunction = function() { alert('here3'); };
})();
第一阶段:编译。编译器看到你已经声明了一个变量someFunction
并创造它。和以前一样,它的值是未定义的。
第二阶段:执行。解释器将一个匿名函数传递给 setTimeout 以便稍后执行。在此函数中,它看到您正在使用变量someFunction
所以它创建了变量的闭包。此时的值someFunction
仍然是未定义的。然后它会看到你分配一个函数给someFunction
。此时的值someFunction
不再是未定义的。 1/100 秒后,setTimeout 触发并调用 someFunction。由于它的值不再是未定义的,因此它可以工作。
案例 4 实际上是案例 2 的另一个版本,其中加入了案例 3 的一些内容。someFunction
被传递给 setTimeout 它已经存在,因为它被声明了。
补充说明:
你可能想知道为什么setTimeout(someFunction, 10)
不会在 someFunction 的本地副本和传递给 setTimeout 的副本之间创建闭包。答案是 JavaScript 中的函数参数始终是,always如果它们是数字或字符串,则按值传递;如果是其他内容,则按引用传递。因此 setTimeout 实际上并没有获取传递给它的变量 someFunction (这意味着要创建一个闭包),而只是获取 someFunction 引用的对象(在本例中是一个函数)。这是 JavaScript 中最广泛使用的机制,用于在发明之前打破闭包(例如在循环中)let
关键词。