更好地理解javascript预编译

Ric*_*ico 5 javascript precompile

var foo=1;
function bar(){
  foo=10;
  return;
  function foo(){}
}
bar();
alert(foo);
Run Code Online (Sandbox Code Playgroud)

我目前正在学习javascript如何在机器中实际运行,这是我在示例中看到的一段代码.我不知道为什么最终警报是1而不是10.所以我想知道任何人都可以帮我解释javascript虚拟机是如何实际执行这些代码的.谢谢!

Cod*_*gue 6

这是由于功能声明提升:

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
Run Code Online (Sandbox Code Playgroud)


T.J*_*der 3

\n

我不知道为什么最终警报是 1 而不是 10。

\n
\n\n

因为foo在这一行中bar

\n\n
foo = 10;\n
Run Code Online (Sandbox Code Playgroud)\n\n

...是稍后在该函数中由函数声明声明的类似变量的东西*:

\n\n
function foo(){}\n
Run Code Online (Sandbox Code Playgroud)\n\n

...不是外面foobar。那是:

\n\n
var foo=1;\nfunction bar(){\n  foo=10;             // <== This `foo`\n  return;\n  function foo(){}    // <== Is the `foo` declared here\n}\nbar();\nalert(foo);\n
Run Code Online (Sandbox Code Playgroud)\n\n

...不是foo在包含范围 ( var foo) 中声明的。

\n\n

发生这种情况有两个原因:

\n\n
    \n
  1. bar在函数中的任何逐步代码之前,函数声明在进入包含范围(在本例中是对 的调用)时立即进行处理。这有时称为“提升”声明(因为它们就像位于最顶层一样发生)。而且由于函数声明不是逐步代码,因此对其return是否被处理没有影响;它会在事情return发生之前得到处理。

  2. \n
  3. 函数声明还创建可能是带有函数名称的变量。因此,foo函数声明中的 有效地成为具有该名称的变量(更多内容见下文)——正如您在该代码中所看到的,您可以为这些“变量”分配新值。

  4. \n
\n\n

当您运行该代码时,JavaScript 引擎执行的操作顺序如下:

\n\n
    \n
  1. 创建一个名为 的变量foo并为其赋予初始值undefined

  2. \n
  3. 创建 function bar,将bar其作为作用域内符号(实际上是变量)添加到当前作用域中,并使其成为对该bar函数的引用。

  4. \n
  5. 启动该范围的分步代码。

  6. \n
  7. 1将值赋给foo.

  8. \n
  9. 调用函数bar

  10. \n
  11. 创建foo与调用相关的函数barfoo在调用期间添加为作用域内符号(实际上是变量)并使其成为对该函数的引用。

  12. \n
  13. 启动该范围的分步代码。

  14. \n
  15. 将值分配10给本地foo(用于引用函数)。

  16. \n
  17. 从函数中返回。

  18. \n
  19. alert在该范围内使用进行调用foo,该范围仍然具有值1

  20. \n
\n\n

您可以在规范的 \xc2\xa710.4.3及其链接到的部分中阅读所有血腥细节。

\n\n
\n\n

* “类似变量的东西” 在 JavaScript 中,每个执行上下文(全局上下文和通过调用函数创建的任何上下文等)都有一个对象,用于保存该上下文中使用的各种名称及其值;它被称为“绑定对象”。上下文的绑定对象(我在这里跳过一些不相关的细节)具有每个变量的属性、函数声明以及其他一些东西,例如arguments伪数组、函数本身的名称(返回函数) ,等等。属性的名称是变量、声明的函数等的名称。这就是为什么分配给fooinside会覆盖对在 中声明的函数的bar引用,而不是分配给外部作用域中的变量。实际上是中的局部变量,即使由于函数声明而未使用 进行声明。foobarfoobarvar

\n