许多编程语言需要一个标记开头的特殊用户编写函数
执行。例如,在C中,此函数必须始终具有名称main()
。在
但是,JavaScript不需要这样的功能。
在JavaScript中缺少这样一个专用的顶级函数的逻辑原因是什么?我知道这是某种理论问题,但我无法在网上找到答案。
答案 0 :(得分:40)
因为整个代码块实际上是一个大的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有两种主要类型的声明:变量声明和函数声明:
在执行任何逐步代码之前,使用var
声明的任何变量的名称都会添加到作用域(值为undefined
)。 (更多:Poor, misunderstood var
)
处理函数声明,并在执行任何逐步代码之前将函数名称添加到作用域。 (JavaScript还有其他东西,称为函数表达式,它是逐步代码。更多内容见下文。)
例如,在这个源文本中:
var x;
x = 1;
foo();
function foo() {
}
声明是
var x;
function foo() {
}
,逐步代码是
x = 1;
foo();
首先处理声明。这就是调用foo
的原因。 (这些相同的规则适用于函数中的源文本。)这种声明处理在其他任何事情之前有时被称为" hoisting,"因为声明在某种意义上从它们在源文本中的位置被移除并且移动到最开始。我更喜欢将它视为两个遍历源:第一个传递执行声明,第二个执行逐步代码。
(旁注:在同一范围内多次声明一个变量是完全合法的[虽然没有意义]。声明两个具有相同名称的函数也是合法的;后一个声明会覆盖前一个函数。)
(附注2:ES2015 [ES6]引入了let
和const
变量声明,其行为与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
)的函数作为逐步代码的一部分。该函数的名称是 not 添加到范围内(如果它是函数声明的话)。
但是,您在很多地方都看不到命名函数表达式,因为JScript(Microsoft的JavaScript引擎)获取它们horribly and utterly wrong,在两个不同的时间创建两个单独的函数。
答案 1 :(得分:2)
JavaScript是事件驱动的,用JavaScript编写的程序没有开始和结束。您可以将它与任何桌面UI工具包进行比较,您可以在其中处理按钮单击和按键操作,但是一旦程序初始化,就没有明显的main
。
例如,在加载页面时会触发window.onload
事件 - 您可以处理该事件。
答案 2 :(得分:2)
在脚本语言中,代码从文件的第一行执行到最后,就好像它被输入到解释器中一样。 (这并不排除解析和编译代码,只要这些进程不会影响所描述的指称语义。)
答案 3 :(得分:-5)
你已经知道了答案
但是,在JavaScript中,不需要这样的功能!
JavaScript是脚本语言,而C需要编译。