为什么使用document.write插入的脚本可以更改变量

时间:2015-06-13 09:14:39

标签: javascript

为什么下面的脚本打印33而不是31?

<script>
var i = 1;
document.write("<script> i=3; document.write(i); </scr" + "ipt>");
document.write(i);
</script>

这个打印31,有什么区别?

<script>
var i = 1;
document.write("<script> i=3; document.write(i); </scr" + "ipt>" + i);
</script>

3 个答案:

答案 0 :(得分:2)

这是它的工作方式:

  1. 您定义名为i的变量,其值为1
  2. 您在文档中编写了一个新脚本

    2.1。此新脚本将i重新定义为3

    2.2。该脚本将i(请记住,现在为3)写入文档。

  3. 您再次写i(从您重新定义它时,值仍然是相同的,3)到文档。

  4. 最终结果为33,因为您曾两次撰写3

  5. 这是第二个块的顺序:

    1. 您使用值i定义变量1
    2. JavaScript连接字符串和i,创建"<script> i=3; document.write(i); </script>1"(注意最后的1,这是连接)
    3. JavaScript将新的连接字符串写入文档。

      3.1。 i被重新定义为3

      3.2。 document.write&#39; i(之前已重新定义为3)。

    4. 由于连接和"3<script> i=3; document.write(i); </script>1",您最终将document.write作为最终文档。很明显,<script>的内容并不可见。所以你最终得到了31

答案 1 :(得分:2)

为了使您的第一个示例的输出为31,在解析器对该输出执行任何操作之前,最外层的脚本块必须一直运行,生成其所有输出。这是一种完全合理的方式来认为它会起作用(我认为这种方式可以使用多年);它只是不正确。 :-)浏览器比这更主动。

浏览器的解析器和JavaScript引擎必须协同处理不使用asyncdefer属性的脚本标记。有两个关键的事情:

  1. 当解析器看到一个完整的script块时,它会突然停止并将其交给JavaScript引擎立即运行。

  2. 调用document.write向解析器提供新令牌;如果解析器看到一个完整的令牌,它将在调用document.write 期间处理。如果该完整令牌是脚本块,则“处理它”涉及在当前调用期间再次调用JavaScript引擎。您可以将每个document.write调用视为对解析器的调用,并将每个完整的脚本块视为对JavaScript引擎的调用。与函数调用类似,这些可以嵌套。

  3. 第二点是你看到33而不是31的原因。你用document.write撰写的剧本在期间执行时调用document.write,而不是之后,因此i3完成之前更新为document.write(并输出)。因此,当您到达主脚本块末尾的document.write时,i已经更改为3,并且已经输出一次。

    在您的第二个示例中,您使用i 之前调用document.write(当您将其附加到文本中时),所以当然您会看到i就像那样,1

    这可能会使通话顺序更加清晰:

    <script>
    document.write("<script>document.write('a');</scr" + "ipt>");
    document.write('b');
    </script>
    

    结果为ab,而不是ba。第一个document.write输出的代码在最后document.write之前运行,因此中间document.write的输出显示在最后一个{{1}}的输出之前。< / p>

答案 2 :(得分:0)

在第一种情况下,在第一种情况下注入document.write重新定义变量i,因此第二种document.write也会打印出来。 在第二种情况下,您只有一个document.write,它将原始值i = 1附加到注入document.write的输出。
有趣的是,有人可能会在这里期待另一种组合&#39; 13&#39;)))