为什么在脚本标签中的“</script>”被视为闭合标签,但是“<h1> hlo </h1>”不显示任何内容?

4
我正在阅读一本书,然后读到了这句话:「HTML解析器不知道你的JavaScript代码是什么,它把它当做其他文本一样处理。」所以如果我们写成这样:
<script type="text/javascript">
      alert("first");
      var string = "</script>";
</script>

我们遇到错误是因为"</script>"作为HTML解析器的闭合标签,而第二行</script>作为脚本结束标记,导致该脚本标签被执行,从而产生了错误。
Uncaught SyntaxError: Unexpected token ILLEGAL

即使第一个 alert() 没有被执行?我不知道为什么?但我的主要问题是,如果 "</script>" 被视为标签,那么当我们写类似这样的内容时:

var str = "<h1> hello world </h1>"; 

那么屏幕上没有显示任何“hello world”?根据之前的例子,HTML解析器应该也会把那个字符串视为HTML标签,但它没有?有人能解释一下吗?对不起我的英语不好 :(

3
因为它位于脚本标签内,根据定义,脚本标签被视为“display: none”。 - slebetman
1
@Tiger:不仅如此。脚本标签的内部内容被视为 CDATA 即文字串。但是在你的第一个示例中,脚本标签在第一个 </script> 结束,因此脚本以未关闭的 " 结束,这是一个编译错误。 - slebetman
3
解析的工作方式如下:如果您看到一个 <script> 标签,逐字扫描文本,直到找到闭合的 </script> 标签。然后将该文本传递给 JavaScript 解释器。由于 DOM 解析器只能识别标记而不能识别 JavaScript 语法,所以标记在字符串声明内部关闭。 - slebetman
3
请将此内容替换为 var string = "</sc" + "ript>";,这样可以使代码更安全,避免被浏览器解析为脚本标签的结束符。 - Loïc Faure-Lacroix
显示剩余9条评论
2个回答

2
您需要了解浏览器的工作原理,以及HTML和Javascript是如何呈现的。以下链接是一个好的阅读资源:How browsers work 下面的文本来自上述链接。 标记算法 该算法的输出是HTML标记。该算法被表达为一个状态机。每个状态消耗输入流的一个或多个字符,并根据这些字符更新下一个状态。决策受当前标记状态和树构建状态的影响。这意味着对于正确的下一个状态,相同的消耗字符将产生不同的结果,具体取决于当前状态。该算法太复杂无法完全介绍,因此让我们看一个简单的例子,以帮助我们理解其主要内容。
基本示例 - 对以下HTML进行标记化:
<html>
    <body>
        Hello world
    </body>
</html>

初始状态为“数据状态”。遇到“<”字符时,状态会更改为“标记打开状态”。消耗“a-z”字符会导致创建一个“开始标记令牌”,状态更改为“标记名称状态”。我们在这个状态下保持,直到消费掉“>”字符。每个字符都附加到新令牌名称上。在我们的情况下,创建的令牌是一个“html”令牌。 当到达“>”标记时,当前令牌将被发射,状态会返回到“数据状态”。 ""标记将按照相同的步骤处理。到目前为止,“html”和“body”标记已经被发出。我们现在回到了“数据状态”。消耗“Hello world”的“H”字符将导致创建并发出字符令牌,这样就一直进行下去,直到到达“”的“<”。对于“Hello world”的每个字符,我们将发出一个字符令牌。 现在我们回到了“标记打开状态”。消耗下一个输入“/”将导致创建一个“结束标记令牌”并移动到“标记名称状态”。同样,我们在此状态下停留,直到达到“>”。然后新的标记令牌将被发射,我们回到“数据状态”。""输入将像先前的情况一样处理。 </script>标记也是一样的。这就是它的工作原理。

谢谢你提供的有用链接,但我认为它并没有回答我的问题。根据你的回答,当浏览器读取<h1>标签时,它的状态应该发生改变,并且应该呈现该元素?但实际上它并没有这样做。 - Tiger

1
我认为我找到了答案,根据https://www.w3.org/TR/html4/types.html#type-cdata,虽然样式和脚本元素使用CDATA作为它们的数据模型,但对于这些元素,用户代理必须以不同的方式处理CDATA。标记和实体必须被视为原始文本并按原样传递给应用程序。字符序列“</”(结束标记开放定界符)的第一次出现被视为终止元素内容的结尾。在有效的文档中,这将是该元素的结束标记。因此,当我们编写以下代码时:
var string = "</script>";

组合符号“< /”被视为结束符,并将文本内容(var string =“)传递给js解释器,我们知道字符串没有正确结束(缺少“),因此会显示错误,然后“)”;被视为文本。根据规范,我们可以将</单词的组合作为终止符来解决这个问题,写成:

var string = "<\/script>";

这里的HTML解析器无法理解JavaScript代码,因此转义序列不起作用。对于HTML解析器,"</"是3个单独的字符,而且还有很多其他变化来打破"<\\"令牌序列。
例如:
var str = "< /script>"; 

(你是否注意到了<和/之间的空格,我不知道它是否符合标准,但它可以工作)

var str = "<" + "/script>";

但还有一件事要记住:

var str = "< /scr" + "pt>" 也可以工作。 (忘记空格,空格对某些人来说很重要。)

因为根据:

https://dev59.com/nHVC5IYBdhLWcg3wnCj6#236106

实际上,浏览器只会在出现真正的闭合标签时才结束解析CDATA脚本块。
谢谢,对于我的英语水平较弱我感到抱歉。

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