var foo=1;
function bar(){
foo=10;
return;
function foo(){}
}
bar();
alert(foo);
我目前正在学习javascript如何在机器中实际运行,这是我在示例中看到的一段代码。我不知道为什么最终警报是1而不是10。所以我想知道任何人都可以帮我解释javascript虚拟机是如何实际执行这些代码的。谢谢!
答案 0 :(得分:6)
这是由于function declaration hoisting:
var foo=1;
function bar(){
function foo(){} // This gets moved up here by the engine
foo=10; // You've reassigned the local `foo` function to 10,
// leaving the global `foo` untouched
return;
}
bar();
alert(foo); // Since the foo has never changed in this scope, it's still 1
答案 1 :(得分:3)
我不知道为什么最后的警报是1而不是10。
因为foo
中此行中的bar
:
foo = 10;
...是稍后在该函数中由函数声明声明的类似变量的东西:
function foo(){}
... 不 foo
以外的bar
。那就是:
var foo=1;
function bar(){
foo=10; // <== This `foo`
return;
function foo(){} // <== Is the `foo` declared here
}
bar();
alert(foo);
...不是在包含范围(foo
)中声明的var foo
。
有两个原因正在发生:
在函数中的任何分步代码之前,函数声明在进入包含范围(在这种情况下为bar
)时立即处理。这有时被称为“提升”声明(因为它们就像它们位于最顶端一样)。由于函数声明不是逐步代码,return
对它是否被处理没有影响;它会在return
发生之前得到处理。
函数声明还可以创建具有函数名称的变量。因此,函数声明中的foo
实际上变为具有该名称的变量(更多信息如下) - 正如您在该代码中看到的那样,您可以为这些“变量”分配新值。
当您运行该代码时,这是JavaScript引擎所做的事情的顺序:
创建一个名为foo
的变量,并为其指定初始值undefined
。
创建函数bar
,在当前作用域中添加bar
作为范围内符号(实际上是变量),并使其成为bar
函数的引用。
启动该范围的分步代码。
将值1
分配给foo
。
致电bar
功能。
创建与foo
调用相关的bar
函数,在调用期间添加foo
作为范围内符号(实际上是变量)并使其成为参考功能。
启动该范围的分步代码。
将值10
分配给本地foo
(用于引用该函数)。
退出函数。
使用该范围内的alert
来调用foo
,其范围仍为1
。
您可以阅读§10.4.3 of the spec及其链接部分中的所有血腥细节。
*“类似变量的东西”在JavaScript中,每个执行上下文(全局上下文和通过调用函数创建的任何上下文等)都有一个对象,用于保存用于的各种名称。这种背景及其价值观;它被称为“绑定对象”。上下文的绑定对象(我在这里跳过一些不相关的细节)对于每个变量,函数声明和一些其他的东西都有属性,例如arguments
伪数组,名称函数本身(参考函数)等。属性的名称是变量的名称,声明的函数等。这就是为什么在foo
内分配bar
会覆盖对foo
中声明的bar
函数的引用。而不是分配给外部范围中的变量。 foo
有效 bar
中的局部变量,即使它未使用var
声明,因为函数声明。
答案 2 :(得分:1)
这与名为 hoisting 的概念有关。 function foo
基本上只是var foo = function ..
的替代语法,因此在bar
内,名称foo
不会引用外部foo
变量,而是引用本地定义的foo
{1}}。此foo
首先是一个函数,但后来被10
覆盖。
现在,通过提升名称foo
是&#34;保留&#34;在代码执行之前,在解析时确定范围。基本上,它执行如下:
function bar(){
var foo = function () {};
foo = 10;
return;
}
因此它根本不会覆盖外部变量。