为什么 JavaScript 不需要 main() 函数?

新手上路,请多包涵

许多编程语言需要一个特殊的用户编写的函数来标记执行的开始。例如,在 C 语言中,此函数的名称必须始终为 main() 。然而,在 JavaScript 中,不需要这样的函数。

JavaScript 中缺少这种专用顶级函数的逻辑原因是什么?我知道这是某种理论问题,但我无法在网上找到答案。

原文由 Michele Palmia 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 650
2 个回答

因为整个代码块实际上是一个大的 main 。在 JavaScript 中,全局代码可以拥有函数代码可以拥有的所有构造,并且可以逐步执行,就像函数一样。事实上,当 JS 引擎将代码块作为一个整体来处理时,它所做的事情与处理函数调用时所做的事情几乎相同。请参阅规范的第 10.4.1 节(“输入全局代码”)和 10.4.3 节(“输入功能代码”)并注意它们的相似之处。

C 不允许在全局级别使用逐步代码(您可以拥有各种初始化程序,并且它们可以逐步执行,但这是一个不同的主题)。因此 C 需要一个明确的入口点( main )。在 JavaScript 中,入口点是代码的开头。


关于您在下面关于全局代码是否是顺序的问题。答案是肯定的,它就像函数中的代码那样。所以:

 var x, y, z;
x = 1;
y = 2;
z = x + y;
alert("z is " + z);

…将提醒 "z is 3" 。代码按顺序运行,从上到下。

不过,在执行逐步代码 之前 会发生一些事情,了解这些事情很有用。最重要的是,在逐步代码开始之前处理输入范围的源文本中的任何 _声明_。 JavaScript 有两种主要类型的声明:变量声明和函数声明:

  1. 在执行任何逐步代码之前,使用 var 声明的任何变量的名称都会添加到作用域(值为 undefined )。 (更多: 可怜,被误解了 var

  2. 在执行任何逐步代码之前,处理函数 声明 并将函数名称添加到作用域。 (JavaScript 还有其他东西,称为函数 _表达式_,它是逐步代码。更多内容见下文。)

因此,例如,在此源文本中:

 var x;
x = 1;
foo();

function foo() {
}

声明是

var x;
function foo() {
}

逐步代码是

x = 1;
foo();

首先处理声明。这就是调用 foo 的原因。 (这些相同的规则适用于函数内的源文本。)这种先于其他任何内容处理声明有时称为“提升”,因为声明在某种意义上从它们在源文本中的位置提升并移动到最开头。我更愿意将其视为两次遍历源代码:第一次进行声明,第二次执行逐步代码。

(旁注:在同一范围内多次声明一个变量是完全合法的[尽管毫无意义]。声明两个具有相同名称的函数也是合法的;后一个声明覆盖了前一个。)

(旁注 2:ES2015 [ES6] 引入了 letconst 变量声明,其行为与 —2fc90d9b0a4c984843ce1d03c9dba8— 变量声明有些不同 你不能用 var 声明一个变量两次他们,他们有块作用域,你不能在声明它的语句之前使用变量。所以他们大多没有提升[有些东西 有点 像提升,因为它们阻止访问包含范围内的阴影变量甚至在 let x 或任何行之前]。)


更多细节,可能会有点技术性:

var

如果 var 发生在逐步代码运行之前,您可能想知道:

 var x = 1;

这是在逐步代码之前发生的,还是作为其中的一部分发生的?答案是,实际上,这只是两种 截然不同的事物 的简写:

 var x;
x = 1;

var x; 部分发生在逐步代码之前, x = 1; 部分是逐步代码,当我们在序列中到达它时执行。所以:

 alert(x); // "undefined" -- there **is** a variable `x`; it has the value `undefined`
var x = 1;
alert(x); // "1" -- now `x` has the value `1`

函数声明

JavaScript 有两个不同但看起来非常相似的东西:函数 声明 和函数 _表达式_。您可以通过是否将结果函数用作定义它的表达式的一部分来判断哪个是哪个。

这是一个函数 _声明_:

 function foo() {
}

这些都是函数 _表达式_(我们使用结果函数值作为表达式的一部分;在计算机科学术语中,函数用作 _右手值_):

 // 1: Assigning the result to something
var x = function() {
};

// 2: Passing the result into a function
bar(function() {
});

// 3: Calling the function immediately
(function(){
})();

// 4: Also calling the function immediately (parens at end are different)
(function(){
}());

// 5: Also calling the function immediately
!function(){
}();

// 6: Syntax error, the parser needs *something* (parens, an operator like ! or
// + or -, whatever) to know that the `function` keyword is starting an *expression*,
// because otherwise it starts a *declaration* and the parens at the end don't make
// any sense (and function declarations are required to have names).
function(){
}();

规则是在逐步代码开始之前处理函数声明。与所有其他表达式一样,函数表达式在遇到它们的地方进行处理。

最后一点:这是一个 命名 函数表达式:

 var f = function foo() {
};

我们将它用作右手值,因此我们知道它是一个表达式;但它有一个像函数声明一样的名字。这是完全有效且合法的 JavaScript,它的目的是创建一个具有适当名称 ( foo ) 的函数作为逐步代码的一部分。函数的名称 不会 添加到作用域中(如果它是函数声明,它就会如此)。

但是,您不会在很多地方看到命名函数表达式,因为 JScript(Microsoft 的 JavaScript 引擎)将它们 弄得非常可怕且完全错误,在两个不同的时间创建了两个独立的函数。

原文由 T.J. Crowder 发布,翻译遵循 CC BY-SA 3.0 许可协议

在脚本语言中,代码从文件的第一行执行到最后,就好像它被输入到解释器中一样。 (这并不排除解析和编译代码,只要这些过程不影响所描述的指称语义即可。)

原文由 Dan D. 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏