为什么函数声明在求值过程中总是移到顶部?
这实际上不是我遇到的具体问题,而是我想自学的问题:
据我所知,在 JavaScript 中,以下代码
if (true) {
function greet(){ alert("Hello!"); }
} else {
function greet(){ alert("Hi!"); }
}
greet();
输出 Hi!
因为代码实际上被评估为某种东西像这样:
greet = function(){ alert("Hello!"); }
greet = function(){ alert("Hi!"); }
if(true){
// possibly a no-op assignment, such as greet = greet?
}else{
// same as the other branch?
}
greet();
从语言设计的角度来看,为什么 JavaScript 会有这样的行为?
This isn't actually a specific issue I have but something I'd like to educate myself on:
As far as I understand, in JavaScript the following code
if (true) {
function greet(){ alert("Hello!"); }
} else {
function greet(){ alert("Hi!"); }
}
greet();
outputs Hi!
because the code is actually evaluated as something like this:
greet = function(){ alert("Hello!"); }
greet = function(){ alert("Hi!"); }
if(true){
// possibly a no-op assignment, such as greet = greet?
}else{
// same as the other branch?
}
greet();
From a language design perspective, why does JavaScript behave in this way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
命名函数是在代码开始之前创建的,因此您不必将函数的声明放在使用它的代码之前:
函数声明在哪里并不重要,即使它位于流程控制结构内,它仍然是在代码开始之前创建的。因此,示例中的 if 语句内根本没有可执行代码,甚至没有无操作赋值。
如果要动态分配函数声明,则必须使用变量:
但不必使用匿名函数,您可以将两个命名函数之一分配给变量。
Named functions are created before the code starts, so that you don't have to put the declaration of a function before the code that uses it:
It doesn't matter where the function declaration is, even if it's inside a flow control structure, it's still created before the code starts. So, there is no executable code at all inside the if statement in your example, not even a no-op assignment.
If you want to assign function declarations dynamically, you have use a variable:
You don't have to use anonymous functions though, you could assign one of two named functions to the variable.
您可能已经发现了该语言的一个怪癖,如果您进一步观察,您还会发现不同的实现。
ECMAScript 处理有两个阶段 - 在第一个阶段,处理所有声明以在其声明范围内创建命名属性。在第二步中,代码被执行。因此,声明的函数和变量从执行开始就在其声明范围内可用,无论它们在代码中的何处声明。
而那些通过赋值创建或赋值的,只有在执行赋值代码时才会有值。
所以:
工作没有错误。这通俗地称为“提升”,意思是声明被“提升”到作用域的顶部。
但是,解释一下您的示例:
对于大多数浏览器,第二个声明会覆盖第一个声明。这是因为 ECMAScript 没有块作用域,只有函数和全局作用域。
但是,在这种情况下,允许对该语言进行扩展。 Firefox 将以上内容视为命名函数表达式,因此显示
true x
。更重要的是,因为其他浏览器将表达式视为声明,所以可以从代码块上方调用 x,但在 Firefox 中则不能,您会收到错误。最重要的是,如果您想有条件地将函数分配给命名参数或变量,请明确执行以下操作:
以便获得一致的行为。您还必须记住,在执行代码之前,x 不会是一个函数,它不会被“提升”,因为它不是一个声明。
You may have found a quirk of the language and if you look a bit further you will also find different implementations.
ECMAScript processing has two phases - in the first, all declarations are processed to create named properties within the scope of their declaration. In the second, the code is executed. So declared functions and variables are available in their declared scope from the start of execution, regardless of where in the code they are declared.
And those that are created or assigned values by assignment will have values only when the assignment code is executed.
So:
works without error. This is colloquially called "hoisting", meaning declarations are "hoisted" to the top of the scope.
However, to paraphrase your example:
For most browsers, the second declaration overrides the first. That is because ECMAScript does not have block scope, only function and global scope.
However, in this case extensions are allowed to the language. Firefox treats the above as named function expressions, hence is shows
true x
. More importantly, because other browsers treat the expressions as declarations, x can be called from above the code block but in Firefox it can't, you'll get an error.The bottom line is if you want to conditionally assign a function to a named parameter or variable, do it explicitly like:
so that you get consistent behaviour. And you must also remember that x will not be a function until after the code has been executed, it will not be "hoisted" because it isn't a declaration.
首先,它并不总是以这种方式表现。对于函数表达式,函数对象仅在内部范围内可用。
其次,当你定义一个函数时,你很可能想在当前作用域中使用它。所以它在当前范围内变得可用是很自然的。考虑封闭范围(取决于函数定义位置)是全局对象或封闭函数体,而不是
if
块。现在,我可以将您的问题简化为以下问题:
为什么使用函数名称作为变量名称?
A:否则开发人员必须始终将函数分配给变量(我更喜欢)。
First, it does not always behave in this way. In the case of Function Expressions function object is only available in the internal scope.
Second, when you define a function it is very likely that you want to use it in the current scope. So it is very natural that it will become available in the current scope. Consider that the enclosing scope (depending on the function definition location) is the global object or the enclosing function body not the
if
block.Now, I can reduce your question to the following question:
Why the function name is used as the variable name?
A : Otherwise developers have to always assign function to a variable (which I prefer).