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



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

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

因为整个代码块实际上是一个大的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变量声明,它们的行为与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(微软的JavaScript引擎)把它们弄错了,在两个不同的时间创建了两个单独的函数。

JavaScript是事件驱动的,用JavaScript编写的程序没有开始和结束。您可以将它与任何桌面UI工具包进行比较,其中处理按钮单击和按键,但是一旦程序初始化,就没有明显的main

例如,有一个window.onload事件,当页面加载时触发-并且您可以处理。

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

Javascript,python和PHP 有脚本语言。这些编程语言不使用main()函数。

你已经知道答案了

在JavaScript中,这样的函数是不需要的!

JavaScript是脚本语言,而C需要编译。

最新更新