更好地理解 javascript 预编译
A better understanding of javascript precompile
var foo=1;
function bar(){
foo=10;
return;
function foo(){}
}
bar();
alert(foo);
我目前正在学习 javascript 在机器中实际上是如何 运行 的,这是我在示例中看到的一段代码。我不知道为什么最后的警报是 1 而不是 10。所以我想知道任何人都可以帮助我解释 javascript 虚拟机实际上是如何执行这些代码的。谢谢!
I got no idea why the final alert is 1 instead of 10.
因为bar
这一行的foo
:
foo = 10;
...是函数声明稍后在该函数中声明的类似变量的东西*:
function foo(){}
...不是 foo
bar
之外的 foo
。即:
var foo=1;
function bar(){
foo=10; // <== This `foo`
return;
function foo(){} // <== Is the `foo` declared here
}
bar();
alert(foo);
...不是在包含范围 (var foo
) 中声明的 foo
。
发生这种情况有两个原因:
函数声明在进入包含作用域时立即处理(在本例中调用 bar
),先于函数中的任何分步代码。这有时被称为 "hoisting" 声明(因为它们就像在最顶层一样发生)。由于函数声明不是逐步代码,因此 return
对其是否被处理没有影响;它在 return
发生之前得到处理。
函数声明还可以创建带有函数名称的变量。因此,函数声明中的 foo
实际上变成了具有该名称的变量(更多内容见下文)——正如您在该代码中所见,您可以为这些 "variables."
分配新值
当您 运行 该代码时,JavaScript 引擎执行的顺序如下:
创建一个名为foo
的变量并赋予它初始值undefined
。
创建函数 bar
,将 bar
添加为当前范围内的范围内符号(实际上是一个变量),并使其成为对 bar
函数。
开始该范围的分步代码。
将值1
赋给foo
。
调用 bar
函数。
创建与调用 bar
相关的 foo
函数,在调用期间添加 foo
作为范围内符号(实际上是一个变量),并且使其成为函数的引用。
启动该范围的分步代码。
将值10
赋值给局部foo
(用于引用函数)。
Returns出函数.
在该范围内使用 foo
调用 alert
,其值仍然为 1
。
您可以在 §10.4.3 of the spec 及其链接的部分中阅读所有详细信息。
* "variable-like thing" 在 JavaScript 中,每个 执行上下文 (全局上下文和通过调用函数等创建的任何上下文)都有一个对象它用于保存在该上下文中使用的各种名称及其值;它被称为 "binding object." 上下文的绑定对象(我在这里跳过了一些不相关的细节)对于每个变量、函数声明和其他一些东西都有 properties arguments
伪数组,函数本身的名称(指回函数),等等。属性的名称是变量的名称、声明的函数等。这就是为什么在 bar
中分配给 foo
会覆盖对 bar
中声明的 foo
函数的引用,而不是分配给外部范围内的变量。 foo
实际上 是 bar
中的局部变量,即使由于函数声明,它没有用 var
声明。
这是由于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
这与一个叫做提升的概念有关。 function foo
本质上只是 var foo = function ..
的替代语法,因此在 bar
内部,名称 foo
不引用外部 foo
变量,而是引用本地定义的 foo
。 foo
首先是一个函数,但后来被 10
覆盖。
现在,通过提升名称 foo
是 "reserved" 并在代码执行之前在解析时限定范围。本质上,它是这样执行的:
function bar(){
var foo = function () {};
foo = 10;
return;
}
因此它根本不会覆盖外部变量。
var foo=1;
function bar(){
foo=10;
return;
function foo(){}
}
bar();
alert(foo);
我目前正在学习 javascript 在机器中实际上是如何 运行 的,这是我在示例中看到的一段代码。我不知道为什么最后的警报是 1 而不是 10。所以我想知道任何人都可以帮助我解释 javascript 虚拟机实际上是如何执行这些代码的。谢谢!
I got no idea why the final alert is 1 instead of 10.
因为bar
这一行的foo
:
foo = 10;
...是函数声明稍后在该函数中声明的类似变量的东西*:
function foo(){}
...不是 foo
bar
之外的 foo
。即:
var foo=1;
function bar(){
foo=10; // <== This `foo`
return;
function foo(){} // <== Is the `foo` declared here
}
bar();
alert(foo);
...不是在包含范围 (var foo
) 中声明的 foo
。
发生这种情况有两个原因:
函数声明在进入包含作用域时立即处理(在本例中调用
bar
),先于函数中的任何分步代码。这有时被称为 "hoisting" 声明(因为它们就像在最顶层一样发生)。由于函数声明不是逐步代码,因此return
对其是否被处理没有影响;它在return
发生之前得到处理。函数声明还可以创建带有函数名称的变量。因此,函数声明中的
分配新值foo
实际上变成了具有该名称的变量(更多内容见下文)——正如您在该代码中所见,您可以为这些 "variables."
当您 运行 该代码时,JavaScript 引擎执行的顺序如下:
创建一个名为
foo
的变量并赋予它初始值undefined
。创建函数
bar
,将bar
添加为当前范围内的范围内符号(实际上是一个变量),并使其成为对bar
函数。开始该范围的分步代码。
将值
1
赋给foo
。调用
bar
函数。创建与调用
bar
相关的foo
函数,在调用期间添加foo
作为范围内符号(实际上是一个变量),并且使其成为函数的引用。启动该范围的分步代码。
将值
10
赋值给局部foo
(用于引用函数)。Returns出函数.
在该范围内使用
foo
调用alert
,其值仍然为1
。
您可以在 §10.4.3 of the spec 及其链接的部分中阅读所有详细信息。
* "variable-like thing" 在 JavaScript 中,每个 执行上下文 (全局上下文和通过调用函数等创建的任何上下文)都有一个对象它用于保存在该上下文中使用的各种名称及其值;它被称为 "binding object." 上下文的绑定对象(我在这里跳过了一些不相关的细节)对于每个变量、函数声明和其他一些东西都有 properties arguments
伪数组,函数本身的名称(指回函数),等等。属性的名称是变量的名称、声明的函数等。这就是为什么在 bar
中分配给 foo
会覆盖对 bar
中声明的 foo
函数的引用,而不是分配给外部范围内的变量。 foo
实际上 是 bar
中的局部变量,即使由于函数声明,它没有用 var
声明。
这是由于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
这与一个叫做提升的概念有关。 function foo
本质上只是 var foo = function ..
的替代语法,因此在 bar
内部,名称 foo
不引用外部 foo
变量,而是引用本地定义的 foo
。 foo
首先是一个函数,但后来被 10
覆盖。
现在,通过提升名称 foo
是 "reserved" 并在代码执行之前在解析时限定范围。本质上,它是这样执行的:
function bar(){
var foo = function () {};
foo = 10;
return;
}
因此它根本不会覆盖外部变量。