67

JavaScript 程序由语句和函数声明组成。执行 JavaScript 程序时,会发生以下两个步骤:

  1. 扫描代码以查找函数声明和每个函数。声明被“执行”(通过创建函数对象)并创建对该函数的命名引用(以便可以从语句中调用该函数)

  2. 语句按顺序执行(评估)(如代码中所示)

正因为如此,这工作得很好

<script>
    foo();
    function foo() {
        return;
    }
</script>

尽管“foo”函数在声明之前被调用,但它之所以有效,是因为函数声明是在语句之​​前评估的。

但是,这不起作用

<script>
    foo();
</script>
<script>
    function foo() {
        return;
    }
</script>

将抛出 ReferenceError(“未定义 foo”)。这导致了这样的结论:网页的 HTML 代码中的每个 SCRIPT 元素都代表一个单独的 JavaScript 程序,并且每次 HTML 解析器遇到一个 SCRIPT 元素时,它都会执行该元素内的程序(然后一旦程序被执行,解析器转到 SCRIPT 元素后面的 HTML 代码)。

再说一次,这确实有效

<script>
    function foo() {
        return;
    }
</script>
<script>
    foo();
</script>

我在这里的理解是全局对象(作为全局执行上下文中的变量对象)一直存在(并且仍然存在),所以第一个 JavaScript 程序会创建函数对象并为它做一个引用,然后第二个 JavaScript 程序将使用该引用来调用该函数。因此,所有 JavaScript 程序(在单个网页内)“使用”同一个 Global 对象,并且一个 JavaScript 程序对 Global 对象所做的所有更改都可以被随后运行的所有 JavaScript 程序观察到。

现在,请注意这...

<script>
    // assuming that foo is not defined
    foo();
    alert(1);
</script>

在上述情况下,警报调用不会执行,因为“foo()”语句会引发 ReferenceError(这会破坏整个 JavaScript 程序),因此所有后续语句都不会执行。

然而,在这种情况下...

<script>
    // assuming that foo is not defined
    foo();
</script>
<script>
    alert(1);
</script>

现在,警报调用确实被执行了。第一个 JavaScript 程序抛出一个 ReferenceError(并因此中断),但第二个 JavaScript 程序正常运行。当然,浏览器会报错(虽然它确实执行了后续的 JavaScript 程序,但在发生错误之后)。

现在,我的结论是:

  • 网页 HTML 代码中的每个 SCRIPT 元素都代表一个单独的 JavaScript 程序。这些程序在 HTML 解析器遇到它们时立即执行。
  • 同一个网页中的所有 JavaScript 程序都“使用”同一个 Global 对象。该 Global 对象始终存在(从获取网页的那一刻起直到网页被销毁)。JavaScript 程序可以操作 Global 对象,一个 JavaScript 程序对 Global 对象所做的所有更改都可以在所有后续 JavaScript 程序中观察到。
  • 如果一个 JavaScript 程序中断(通过抛出错误),这不会阻止后续 JavaScript 程序执行。

请事实检查这篇文章,如果我有什么问题,请告诉我。

另外,我还没有找到解释这篇文章中提到的行为的资源,我认为浏览器制造商一定在某个地方发布了这些资源,所以如果你知道它们,请提供它们的链接。

4

4 回答 4

19

函数提升——在函数的其余部分之前评估function语句的过程——是 ECMAScript 标准 IIRC 的一部分(我现在找不到参考资料,但我记得看到提到它的 EMCAScript 讨论)。标签的评估script是 HTML 标准的一部分。它并没有用很多词来说明它们是“单独的程序”,但它确实说脚本元素按照它们在文档中出现的顺序进行评估。这就是后来脚本标签中的函数没有被提升的原因:脚本还没有被评估。这也解释了为什么一个脚本停止并不会切断后续脚本:当当前脚本停止评估时,下一个脚本开始。

于 2010-09-17T18:25:09.270 回答
15

Dmitry Soshnikov 已经回答了你的问题。正如 ECMAScript 规范所定义的,每个<script>元素都作为程序执行。单个页面中的每个程序都使用一个全局对象。就是这样。

于 2010-10-19T11:18:39.283 回答
7

它们是独立的程序,但它们修改一个共享的全局对象。

于 2010-09-17T17:05:28.537 回答
4

另一种思考方式是伪本地与全局范围。每个 SCRIPT 声明都有其当前方法/函数的本地范围,以及对当前(先前声明的)全局范围的访问。每当在 SCRIPT 块中定义方法/函数时,它就会被添加到全局范围中,并且可以被之后的 SCRIPT 块访问。

此外,这是W3C关于脚本声明/处理/修改的进一步参考:

文档的动态修改可以建模如下:

  1. 所有 SCRIPT 元素都会在文档加载时按顺序进行评估。
  2. 对给定 SCRIPT 元素中生成 SGML CDATA 的所有脚本结构进行评估。它们组合生成的文本被插入到文档中以代替 SCRIPT 元素。
  3. 生成的 CDATA 被重新评估。

是另一个关于脚本/函数评估/声明的好资源。

于 2010-10-20T21:27:36.753 回答