JavaScript学习笔记 ——HTML界面解析顺序、JavaScript定时机制

    之前学习的JavaScript知识总体来说还是太浅了,一些代码甚至看不太懂,更别提去写出质量高的JavaScript代码。这次的笔记主要是根据《JavaScript高级程序设计》和《JavaScript Dom编程艺术》中的知识来阐述最近学到的知识和自己的见解。


    第一章首先讲述的是JavaScript的发展,在《JavaScript高级程序设计》第三版出版时还没有诞生ES6的语法,现在ES6却已经是使用非常普遍。

    文档对象模型(DOM)将 HTML 文档表达为树结构,每一个页面的组成部分都是一个DOM节点,开发者通过DOM可以访问甚至是操作HTML 元素的对象和属性。


书上有这样一段话:

包含在元素内部的JavaScript代码将从上至下依次解释…解释器会解释一个函数的定义,然后将该定义保存在自己的环境中。在解释器对<script>元素内部的所有代码求值完毕之前,页面中其余内容都不会被浏览器加载或显示。

先看看代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>1</title>
  </head>
  <script>
    let changeToRed = function() {
      console.log("33333333");
      document.getElementById("red").style.color = "red";
    };

    let changeToGreen = function() {
      console.log("44444444");
      document.getElementById("green").style.color = "green";
    };
    console.log("1111111");
    setTimeout(changeToRed, 5000);
    setTimeout(changeToGreen,3000)
    console.log("222222");
  </script>
  <body>
    <p>这是正常文字</p>
    <p id="red">这是红色的文字</p>
    <p id="green">这是绿色的文字</p>
    <p>这是最后文字</p>
  </body>
</html>

上面的代码,我对它预期的执行顺序是:

界面: 这是正常文字(黑色) --> 这是红色的文字(先是黑色,5秒后变红色) --> 这是绿色的文字(先是黑色,再过3秒后变绿色)–> 这是最后文字

控制台:1111111 --> 33333333 --> 44444444 --> 22222222

实际上:

界面: 这是正常文字(黑色) , 这是红色的文字(黑色) 、 这是绿色的文字(黑色)、这是最后文字(黑色)

–> 这是绿色的文字 约3秒后变绿色 --> 这是红色的文字再过约2秒后变红色;

控制台:1111111 、2222222 --(约3秒后)–> 44444444 --(约2秒后)–> 3333333

可能是我对前面那段话还有JavaScript的定时机制和页面解析顺序有点误解。

带着问题继续探索。


为了进行对比验证,不妨再运行如下代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>2</title>
    <script type="text/javascript">
      let changeToRed = function() {
      console.log("33333333");
      document.getElementById("red").style.color = "red";
    };

    let changeToGreen = function() {
      console.log("44444444");
      document.getElementById("green").style.color = "green";
    };
    setTimeout(changeToRed, 5000);
    console.log("55555555");
    </script>
  </head>
  <script>
    console.log("1111111");
    setTimeout(changeToGreen,3000)
    console.log("222222");
  </script>
  <body>
    <p>这是正常文字</p>
    <p id="red">这是红色的文字</p>
    <p id="green">这是绿色的文字</p>
    <p>这是最后文字</p>
  </body>
</html>

这段代码的运行结果为:

界面: 这是正常文字(黑色) , 这是红色的文字(黑色) 、 这是绿色的文字(黑色)、这是最后文字(黑色)

–> 这是绿色的文字3秒后变绿色 --> 这是红色的文字再过约2秒后变红色;

控制台:55555555、1111111 、2222222 --(约3秒后)–> 44444444 --(约2秒后)–> 3333333

这个时候我们可以明确的是,对于<html>标签内的代码,会从上到下解析。对于<script>标签内的代码,也是按顺序从上到下进行解释。那么页面渲染和JavaScript代码的执行的顺序又是怎么样的呢?

再看下面的代码:

<!DOCTYPE html>
<html lang="en">
  <head onload="console.log('66666666');">
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>3</title>
  </head>
  <script>
    let changeToRed = function() {
      console.log("33333333");
      document.getElementById("red").style.color = "red";
    };
    let changeToGreen = function() {
      console.log("44444444");
      document.getElementById("green").style.color = "green";
    };
    console.log("1111111");
    setTimeout(changeToGreen,3000)
    console.log("222222");   
    setTimeout(changeToRed, 5000);
    console.log("55555555");
  </script>
  <body onload="console.log('77777777');">
    <p>这是正常文字</p>
    <p id="red">这是红色的文字</p>
    <p id="green">这是绿色的文字</p>
    <p>这是最后文字</p>
  </body>
</html>

这段代码的运行结果为:

界面: 这是正常文字(黑色) , 这是红色的文字(黑色) 、 这是绿色的文字(黑色)、这是最后文字(黑色)

–> 这是绿色的文字3秒后变绿色 --> 这是红色的文字再过约2秒后变红色;

控制台:1111111 、2222222 、55555555、77777777 --(约3秒后)–> 44444444 --(约2秒后)–> 3333333

可以看到,JavaScript代码中直接输出在控制台的语句总是最先执行。你也可能会说:“当然啊!因为你的<script> 放在了<body> 前面呀!…”

我测试了一下,把<script> 代码换到<body> 标签之后,或者是<body> 标签内部,甚至是<head> ,结果都是一样的,所以,浏览器加载界面时,总是会先去解释<script> 中的代码。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>4</title>
  </head>
    <body onload="let temp = '22222222'">
  </body>
  <script>
      console.log(temp);
    </script>
</html> 

运行:Uncaught ReferenceError: temp is not defined at 4.html:12

上面这两个例子则更直接地证实了之前的结论。

    但是,对于之前那一段代码,为什么同样在<script> 中输出到控制台的33333333和44444444却在加载<body> 之后才执行呢?为什么执行完变绿的样式之后,只隔了2秒而不是5秒就执行变红样式了呢?

不妨先来看看JavaScript的定时器机制。


JavaScript定时器与执行

JavaScript可以通过setTimeout()来实现定时操作。

setTimeout(code,millisec,lang):在指定的毫秒数之后执行函数或计算表达式。

参数 描述
code 必需。等待执行的 JavaScript 代码。
millisec 必需。在执行代码前需等待的毫秒数。
lang 可选。脚本语言可以是:JScript /VBScript/ JavaScript。

    JavaScript的定时器不是一个线程,因为JavaScript是运行于单线程环境中的,而定时器只是计划代码在未来的某个时间执行。可以这么理解:浏览器负责进行排序,指派某段代码在某个时间点应该按照什么样的顺序执行。对于指定的时间间隔,它表示的是何时将定时器的代码添加到执行队列,而不是何时执行代码。

    在其他地方看到了很多解释关于浏览器进程的一些理解,其中包括浏览器进程是多线程,包括了GUI渲染线程、JS引擎线程、事件触发线程、定时触发器线程、异步http请求线程等主要线程。但我更倾向于《JavaScript高级程序设计》这本书中的说法(或许是比较权威吧)。

    再结合前面的代码,可以这么理解:浏览器首先把解析JavaScript中的代码放到任务列表,另外,渲染界面(即解析`` 等其中的代码)也被放入队列。队列中的任务都要等待JavaScript进程空闲之后才能执行,并且是按照顺序执行。 首先是解析JavaScript代码,当解析到setTimeout(changeToGreen,3000); 这一行时,本应等待3秒之后执行这行代码,但是JavaScript进程这个时候是空闲状态,因此会继续向下执行解析,解析完之后,如果还是空闲,就执行完全部的JavaScript代码,并且开始渲染页面。而在3秒钟之后,将这个任务放入任务队列。当解析到setTimeout(changeToRed, 5000); 也一样,在5秒后将这个任务放入队列。解析JavaScript代码和渲染页面的速度很快,所以几乎是从加载HTML页面开始,第3秒后放入任务队列,此时任务队列早已为空,JavaScript进程会立刻执行这一任务。


总结

  • 对于<html>标签内的代码,会从上到下解析。

    首先是<header>标签中的代码,head标签中可能会包含对外部文件的引用,从开始运行就会下载这些被引用的外部文件。然后解析<body> 标签中的代码,如果此时``中引用的外部文件没有下载完,将会继续下载。

  • 对于<script>标签内的代码,其按照从上之下的顺序解释。`` 标签可以出现在HTML页面的任何位置。浏览器将控制权交给JavaScript的解释器,如果<script> 标签引用了外部脚本文件,就下载该脚本,否则就直接执行,执行完毕后将控制权返回,浏览器继续渲染页面。由于是单线程,在渲染界面的时候不会执行JS代码,在执行JS代码的时候也不会渲染界面。

    欢迎批评指正!

猜你喜欢

转载自blog.csdn.net/qq_38154018/article/details/106250304