更好地理解 javascript 预编译
Posted
技术标签:
【中文标题】更好地理解 javascript 预编译【英文标题】:A better understanding of javascript precompile 【发布时间】:2015-04-21 11:36:12 【问题描述】:var foo=1;
function bar()
foo=10;
return;
function foo()
bar();
alert(foo);
我目前正在学习 javascript 是如何在机器中实际运行的,这是我在示例中看到的一段代码。我不知道为什么最终警报是 1 而不是 10。所以我想知道任何人都可以帮助我解释 javascript 虚拟机是如何实际执行这些代码的。谢谢!
【问题讨论】:
【参考方案1】:这是由于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
【讨论】:
如果在bar
中声明foo
被移动 到顶部,然后应用相同的行为,bar
的声明不会在var foo=1;
之前移动(真正的问题,而不是修辞问题)?
@Ejay 正确。这意味着如果我调用bar()
之前 function bar()
,代码仍然会成功。【参考方案2】:
我不知道为什么最后的警报是 1 而不是 10。
因为bar
这一行中的foo
:
foo = 10;
...是稍后在该函数中由函数声明声明的类似变量的事物:
function 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
),在函数中的任何分步代码之前。这有时被称为“提升”声明(因为它们就像它们在最顶端一样发生)。而且由于函数声明不是一步一步的代码,return
对它是否被处理没有影响;它在return
发生之前得到处理。
函数声明还可以创建带有函数名称的变量。因此,函数声明中的 foo
实际上变成了具有该名称的变量(更多内容见下文)——正如您在该代码中看到的那样,您可以为这些“变量”分配新值。
当您运行该代码时,JavaScript 引擎执行的操作顺序如下:
创建一个名为foo
的变量,并为其赋予初始值undefined
。
创建函数bar
,在当前作用域中添加bar
作为作用域内符号(实际上是一个变量),并使其成为对bar
函数的引用。
开始该范围的分步代码。
将值1
分配给foo
。
调用bar
函数。
创建与对bar
的调用相关的foo
函数,在调用期间添加foo
作为范围内符号(实际上是一个变量),并使其成为对该函数的引用。
启动该范围的分步代码。
将值10
赋值给本地的foo
(用于引用函数)。
从函数中返回。
在该范围内使用foo
调用alert
,其值仍为1
。
您可以阅读§10.4.3 of the spec 中的所有血腥细节及其链接的部分。
* “类变量的东西” 在 JavaScript 中,每个执行上下文(全局上下文和调用函数创建的任何上下文等)都有一个对象,用于保存在该背景及其价值观;它被称为“绑定对象”。上下文的绑定对象(我在这里跳过了一些不相关的细节)为每个变量、函数声明和其他一些东西(例如 arguments
伪数组、名称函数本身(指回函数),等等。属性的名称是变量的名称、声明的函数等。这就是为什么在bar
中分配给foo
会覆盖对bar
中声明的foo
函数的引用,而不是分配给外部范围。 foo
实际上是bar
中的一个局部变量,即使它没有用var
声明,因为函数声明。
【讨论】:
【参考方案3】:这与称为提升的概念有关。 function foo
本质上只是var foo = function ..
的替代语法,因此在bar
内部,名称foo
不是指外部foo
变量,而是指本地定义的foo
。这个foo
最初是一个函数,但后来被10
覆盖。
现在,通过提升名称 foo
是“保留的”并在解析时,在代码执行之前限定范围。本质上,它是这样执行的:
function bar()
var foo = function () ;
foo = 10;
return;
因此它根本不会覆盖外部变量。
【讨论】:
以上是关于更好地理解 javascript 预编译的主要内容,如果未能解决你的问题,请参考以下文章