在浏览器中为一个网页执行了多少个JavaScript程序?


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程序都可以观察到一个JavaScript程序对Global对象所做的所有更改。

现在,请注意...

<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对象。该全局对象始终存在(从抓取网页的那一刻直到网页被破坏)。JavaScript程序可以操纵Global对象,并且可以在所有后续JavaScript程序中观察到一个JavaScript程序对Global对象所做的所有更改。
  • 如果一个JavaScript程序中断(抛出错误),则不会阻止后续的JavaScript程序执行。

请事实检查这篇文章,并告诉我是否有错。

另外,我还没有找到解释这篇文章中提到的行为的资源,并且我认为浏览器制造商必须在某处发布了这些资源,因此,如果您了解这些资源,请提供指向它们的链接。


1
我从您的问题中删除了答案部分,因为它不属于那里。请随时将其张贴为正确答案。
科迪·格雷

Answers:


15

德米特里·索什尼科夫(Dmitry Soshnikov)回答了您的问题。每个<script>元素都按照ECMAScript规范的定义作为程序执行。单个页面中的每个程序都使用一个全局对象。就是这样。


19

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




4

对此进行考虑的另一种方法是伪局部与全局范围。每个SCRIPT声明都有其当前方法/函数的本地范围,以及对当前(先前声明的)全局范围的访问。只要在SCRIPT块中定义了方法/功能,就会将其添加到全局范围,并在其后被SCRIPT块访问。

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

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

  1. 加载文档时,将按顺序评估所有SCRIPT元素。
  2. 将评估给定SCRIPT元素内生成SGML CDATA的所有脚本构造。它们的组合生成的文本将代替SCRIPT元素插入文档中。
  3. 重新评估生成的CDATA。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.