JavaScript 函数声明和求值顺序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3887408/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JavaScript function declaration and evaluation order
提问by We Are All Monica
Why does the first one of these examples not work, but all the other ones do?
为什么这些例子中的第一个不起作用,但所有其他例子都起作用?
// 1 - does not work
(function() {
setTimeout(someFunction1, 10);
var someFunction1 = function() { alert('here1'); };
})();
// 2
(function() {
setTimeout(someFunction2, 10);
function someFunction2() { alert('here2'); }
})();
// 3
(function() {
setTimeout(function() { someFunction3(); }, 10);
var someFunction3 = function() { alert('here3'); };
})();
// 4
(function() {
setTimeout(function() { someFunction4(); }, 10);
function someFunction4() { alert('here4'); }
})();
回答by slebetman
This is neither a scope problem nor is it a closure problem. The problem is in understanding between declarationsand expressions.
这既不是范围问题,也不是闭包问题。问题在于声明和表达式之间的理解。
JavaScript code, since even Netscape's first version of JavaScript and Microsoft's first copy of it, is processed in two phases:
JavaScript 代码,因为即使是 Netscape 的第一个 JavaScript 版本和 Microsoft 的第一个副本,也是分两个阶段处理的:
Phase 1: compilation - in this phase the code is compiled into a syntax tree (and bytecode or binary depending on the engine).
阶段 1:编译 - 在这个阶段,代码被编译成语法树(字节码或二进制取决于引擎)。
Phase 2: execution - the parsed code is then interpreted.
阶段 2:执行 - 然后解释解析的代码。
The syntax for function declarationis:
函数声明的语法是:
function name (arguments) {code}
Arguments are of course optional (code is optional as well but what's the point of that?).
参数当然是可选的(代码也是可选的,但这有什么意义呢?)。
But JavaScript also allows you to create functions using expressions. The syntax for function expressions are similar to function declarations except that they are written in expression context. And expressions are:
但是 JavaScript 也允许您使用表达式创建函数。函数表达式的语法类似于函数声明,只是它们是在表达式上下文中编写的。和表达式是:
- Anything to the right of an
=sign (or:on object literals). - Anything in parentheses
(). - Parameters to functions (this is actually already covered by 2).
=符号右侧的任何内容(或:对象字面量)。- 括号中的任何内容
()。 - 函数的参数(这实际上已经被 2 覆盖了)。
Expressionsunlike declarationsare processed in the execution phase rather than the compilation phase. And because of this the order of expressions matter.
与声明不同的表达式在执行阶段而不是编译阶段进行处理。正因为如此,表达式的顺序很重要。
So, to clarify:
所以,澄清一下:
// 1
(function() {
setTimeout(someFunction, 10);
var someFunction = function() { alert('here1'); };
})();
Phase 1: compilation. The compiler sees that the variable someFunctionis defined so it creates it. By default all variables created have the value of undefined. Note that the compiler cannot assign values yet at this point because the values may need the interpreter to execute some code to return a value to assign. And at this stage we are not yet executing code.
第一阶段:编译。编译器看到变量someFunction已定义,因此创建它。默认情况下,所有创建的变量都具有未定义的值。请注意,此时编译器还不能赋值,因为这些值可能需要解释器执行一些代码来返回要赋值的值。在这个阶段,我们还没有执行代码。
Phase 2: execution. The interpreter sees you want to pass the variable someFunctionto setTimeout. And so it does. Unfortunately the current value of someFunctionis undefined.
阶段 2:执行。解释器看到您想将变量传递someFunction给 setTimeout。确实如此。不幸的是,当前的值someFunction是未定义的。
// 2
(function() {
setTimeout(someFunction, 10);
function someFunction() { alert('here2'); }
})();
Phase 1: compilation. The compiler sees you are declaring a function with the name someFunction and so it creates it.
第一阶段:编译。编译器看到您正在声明一个名为 someFunction 的函数,因此它创建了它。
Phase 2: The interpreter sees you want to pass someFunctionto the setTimeout. And so it does. The current value of someFunctionis its compiled function declaration.
第 2 阶段:解释器看到您要传递someFunction给 setTimeout。确实如此。的当前值someFunction是其编译后的函数声明。
// 3
(function() {
setTimeout(function() { someFunction(); }, 10);
var someFunction = function() { alert('here3'); };
})();
Phase 1: compilation. The compiler sees you have declared a variable someFunctionand creates it. As before, its value is undefined.
第一阶段:编译。编译器看到您声明了一个变量someFunction并创建它。和以前一样,它的值是未定义的。
Phase 2: execution. The interpreter passes an anonymous function to setTimeout to be executed later. In this function it sees you're using the variable someFunctionso it creates a closure to the variable. At this point the value of someFunctionis still undefined. Then it sees you assigning a function to someFunction. At this point the value of someFunctionis no longer undefined. 1/100th of a second later the setTimeout triggers and the someFunction is called. Since its value is no longer undefined it works.
阶段 2:执行。解释器将匿名函数传递给 setTimeout 以供稍后执行。在此函数中,它看到您正在使用该变量,someFunction因此它为该变量创建了一个闭包。此时,的值someFunction仍未定义。然后它会看到您将函数分配给someFunction. 此时,的值someFunction不再是未定义的。1/100 秒后 setTimeout 触发并调用 someFunction。由于它的值不再是未定义的,因此它可以工作。
Case 4 is really another version of case 2 with a bit of case 3 thrown in. At the point someFunctionis passed to setTimeout it already exists due to it being declared.
案例 4 实际上是案例 2 的另一个版本,其中加入了一些案例 3。此时someFunction传递给 setTimeout 它已经存在,因为它被声明了。
Additional clarification:
补充说明:
You may wonder why setTimeout(someFunction, 10)doesn't create a closure between the local copy of someFunction and the one passed to setTimeout. The answer to that is that function arguments in JavaScript are always, alwayspassed by value if they are numbers or strings or by reference for everything else. So setTimeout does not actually get the variable someFunction passed to it (which would have meant a closure being created) but rather only gets the object that someFunction refers to (which in this case is a function). This is the most widely used mechanism in JavaScript for breaking closures (for example in loops).
您可能想知道为什么setTimeout(someFunction, 10)不在 someFunction 的本地副本和传递给 setTimeout 的副本之间创建一个闭包。答案是 JavaScript 中的函数参数总是按值传递,如果它们是数字或字符串,或者是其他所有内容的引用。所以 setTimeout 实际上并没有获取传递给它的变量 someFunction (这意味着创建了一个闭包),而是只获取了 someFunction 引用的对象(在这种情况下是一个函数)。这是 JavaScript 中最广泛使用的打破闭包的机制(例如在循环中)。
回答by Javier
Javascript's scope is function based, not strictly lexical scoping. that means that
Javascript 的范围是基于函数的,而不是严格的词法范围。这意味着
Somefunction1 is defined from the start of the enclosing function, but it's content is undefined until assigned.
in the second example, the assignment is part of the declaration, so it 'moves' to the top.
in the third example, the variable exist when the anonymous inner closure is defined, but it's not used until 10 seconds later, by then the value has been assigned.
fourth example has both of the second and third reasons to work
Somefunction1 是从封闭函数的开始定义的,但它的内容在分配之前是未定义的。
在第二个例子中,赋值是声明的一部分,所以它“移动”到顶部。
在第三个示例中,该变量在定义匿名内部闭包时存在,但直到 10 秒后才使用,此时已分配值。
第四个例子有第二个和第三个工作原因
回答by matt b
Because someFunction1has not yet been assigned at the time the call to setTimeout()is executed.
因为someFunction1在setTimeout()执行调用时尚未分配。
someFunction3 may look like a similar case, but since you are passing a function wrapping someFunction3()to setTimeout()in this case, the call to someFunction3()is not evaluated until later.
someFunction3 可能看起来像一个类似的情况,但由于在这种情况下您正在传递一个函数包装someFunction3()到setTimeout(),因此someFunction3()直到稍后才会评估对的调用。
回答by Terry Prothero
This sounds like a basic case of following good procedure to stay out of trouble. Declare variables and functions before you use them and declare functions like this:
这听起来像是遵循良好程序以远离麻烦的基本案例。在使用变量和函数之前声明它们,并像这样声明函数:
function name (arguments) {code}
Avoid declaring them with var. This is just sloppy and leads to problems. If you get into the habit of declaring everything before using it, most of your problems will disappear in a big hurry. When declaring variables, I would initialize them with a valid value right away to insure that none of them are undefined. I also tend to include code that checks for valid values of global variables before a function uses them. This is an additional safeguard against errors.
避免使用 var 声明它们。这只是草率并导致问题。如果你养成在使用它之前声明一切的习惯,你的大部分问题都会很快消失。在声明变量时,我会立即用一个有效值初始化它们,以确保它们都不是未定义的。我还倾向于包含在函数使用全局变量之前检查全局变量的有效值的代码。这是防止错误的额外保护措施。
The technical details of how all this works is sort of like the physics of how a hand grenade works when you play with it. My simple advice is to not play with hand grenades in the first place.
所有这些工作原理的技术细节有点像你玩手榴弹时的物理原理。我的简单建议是首先不要玩手榴弹。
Some simple declarations at the beginning of the code might solve most most of these kinds of problems, but some cleanup of the code might still be necessary.
代码开头的一些简单声明可能会解决大多数此类问题,但可能仍然需要对代码进行一些清理。
Additional Note:
I ran a few experiments and it seems that if you declare all of your functions in the manner described here, it doesn't really matter what order they are in. If function A uses function B, function B does not have to be declared before function A.
附加说明:
我进行了一些实验,似乎如果您以此处描述的方式声明所有函数,它们的顺序并不重要。如果函数 A 使用函数 B,则函数 B 不必在函数 A 之前声明。
So, declare all of your functions first, your global variables next, and then put your other code last. Follow these rules of thumb and you can't go wrong. It might even be best to put your declarations in the head of the web page and your other code in the body to ensure enforcement of these rules.
因此,首先声明所有函数,然后是全局变量,然后将其他代码放在最后。遵循这些经验法则,您就不会出错。最好将您的声明放在网页的头部,将其他代码放在正文中,以确保执行这些规则。

