为什么在将函数分配给window.onload时脚本顺序很重要?

3

I have a website with three files:

index.html

<html>
<head>
<script src="First.js"></script>
<script src="Second.js"></script>
</head>
<body></body>
</html>

First.js

window.onload = Main;

Second.js

function Main() { var foo = 1; }

当我打开页面时,我期望变量'foo'被设置为'1'。然而,当我打开页面时,它会崩溃,并显示“Main未定义”,并且'Main()'从未被调用。
如果窗口的'.onload'事件不应该在页面完全加载之前触发,因此假定两个脚本都已加载,那么为什么窗口对象没有对'Second.js'中'Main()'方法的引用? 'Main()'应该是一个全局可访问的函数吗?
注意:我意识到我可以更改加载脚本的顺序,然后我的代码可以正常工作,但这不是我的问题的目的。我真正想做的是验证我的假设。

你能否发布包含这两个文件的HTML文件? - Saebekassebil
3个回答

2
它不起作用是因为<script>元素的代码是按顺序和独立地运行的(这假定是同步脚本,即默认情况下)。也就是说,两个不同的<script>部分不能互相引用。现在,JavaScript会在同一上下文中“提升”function name () {}结构,所以这将在单个<script>中“乱序”工作。

前言/警告:

考虑不要使用onload。如果其他东西尝试使用它(onload),那么可能会出现故障。(我建议使用jQuery,因为它“正确地解决了这个问题”并且“非常容易”……当然,许多其他框架也是如此。选择一个:-)

例如,这将在jQuery中工作:

jQuery(function () { Main() })

为什么它有效(以及如何在不重新排序脚本或使用jQuery的情况下修复它)的解释:

请注意,在这种情况下,调用了jQuery内部事件处理程序,该处理程序调用匿名回调,然后调用Main(现在已解析为函数对象)。类似的代码如下:

window.onload = function () { Main() }

这些代码之所以有效,是因为直到onload事件发生时才会执行Main,在这种情况下,所有[同步的]<script>元素都已被执行。(请参见我的评论,了解评估Main的含义。)
另一方面,window.onload = Main(或jQuery(Main))会立即评估Main并使用结果值;正如其他人所指出的那样,由于<script>元素的顺序(它们按顺序运行),此时Main未设置(“未定义”)。
请参考顶部的注释,了解为什么直接使用onload ;-)
愉快的编码。

我认为,当你将一个变量设置为一个没有括号的函数时,它不会被计算,只是简单地存储对它的引用(例如:'var x = Main;')。如果你想要计算'Main()',你应该这样做:'var x = Main();'。假设这是正确的,那么将一个值分配给'window'对象的'onload'事件是否也适用于此?或者说,对事件的赋值总是导致所分配的内容被计算吗?如果'window.onload = Main;'计算'Main()',那么'window.onload = Main();'又会做什么呢? - chopperdave
@chopperdave 不是的 :) 标识符被评估为一个值,然后该值被用作表达式中的任何操作数。例如:(functionName)()functionName() 具有相同的语义。首先,functionName评估,然后将 ()(函数应用)运算符应用于结果值(必须是函数对象,以避免出现棘手的异常)。正是 () 调用了之前表达式的求值结果(在本例中为 functionName)的函数对象。在 JavaScript 中,函数是一级值。 - user166390
有趣,但我认为我需要一些示例来说明。 我们将在聊天中讨论,然后或许我们可以更新你的回答以澄清。 - chopperdave
@chopperdave 寻找关于“自调用”JavaScript函数的SO问题;有一些非常棒的例子和好的解释。一个非常琐碎的例子是:(function () {return "hello"})() 函数对象(一个值),由表达式 function () {return "hello"}评估结果调用最终的() - user166390
看起来你不能主动与某人开始聊天或私信,所以我猜你应该使用评论来进行这种来回交流 :) - chopperdave

2
第二个脚本文件应该首先被包含进来!在那里定义了函数。在第一个脚本中无法引用它,直到它被加载完毕。

我意识到这是一个解决方法,使我的代码按原样工作,但这并不是我问题的目的。 目的在于了解为什么我的假设是错误的。 - chopperdave

1

Main() 函数在 Second.js 被加载之前是不存在的,因此如果你先加载 First.js,那么在 window.onload = Main; 中就没有可用的函数对象(Main 是未定义的)。如果还没有加载 Second.js,请先加载它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接