前言
本文是阅读《JavaScipt忍者秘籍》的心得体会
- 浏览器是否会总是根据给定的html来渲染页面?
- web 应用一次能处理多少个事件?
- 为什么浏览器使用事件队列来处理事件?
一、web应用的生命周期
典型的生命周期当用户输入url,浏览器向服务器发送了请求,服务器接收了请求并且返回一个由html,css,hmtl组成的响应,浏览器接收了该响应后就开始了。
然后浏览器开始根据响应构建页面创建用户界面。然后开始做事件处理,进入事件队列的循环中,等待事件的发生,然后调用处理器。
应用的生命周期随这用户关掉或者离开界面结束。
二、如何构建页面?
- 解析HTML代码并构建文档对象模型(DOM)
- 执行JavaScript代码
在页面构建过程中这两个步骤会交替执行多次。
#HTML解析和DOM构建
浏览器通过解析接收到的HTML代码构建一个个HTML元素来构建DOM。
在这个过程中每一个HTML元素都被当成一个节点,除了根节点外每一个节点都只有一个父节点,可以有任意数量个子节点,同一个元素节点的子节点被称作兄弟节点。
以HTML为蓝本渲染DOM的时候还会自动修复一些错误。
<html>
<head>
<div></div>
</head>
<body>
<body>
</html>
复制代码
在这里我们把内容元素div放到了header中。
浏览器自动把它调整到了body中
直到我们遇到了一种特殊类型的HTML元素——脚本元素,脚本元素中包括JavaScript代码,当咱们遇到了这种元素就必须停止DOM的渲染开始执行JavaScript代码。
#执行JavaScript代码
js代码由浏览器引擎执行,firefox的Spidemonkey引擎,Chrome和Opera 和V8引擎和IE的Chakra引擎。js代码的目的是提供动态页面,浏览器通过全局对象提供了api使JavaScript引擎可以与之交互并改变其内容。
#JavaScript中的全局对象
JavaScript对象中存在一个window对象,它代表了整个包含页面的窗口。
它最重要的属性是document,代表了当前页面的dom。
window对象是老大,通过跟老大接触我们可以获取其他所有的全局对象,全局变量和浏览器API的访问途径。我们可以在任何程度上改变DOM,修改,删除,添加节点。
#全局代码和函数代码
位置上函数外的是全局变量,函数内的函数变量。
执行方式上去全局代码会由JavaScript引擎以一种直接的方式自动执行,要想执行函数代码就要被其他代码调用,可以是全局代码,也可以是其他函数,又或者是被浏览器调用。
在页面构建阶段,如果遇到了脚本节点,浏览器就会停止从HTML到DOM的节点,开始执行JavaScript代码。看如下代码
<body>
<button id="button1" type="button"></button>
<!--创建一个函数,点击添加元素,给元素添加内容插入到dom节点。-->
<script>
function createButton(element, content) {
var createMessage = window.document.createElement('div');
createMessage.textContent = content;
element.appendChild(createMessage);
}
var button1 = document.getElementById('button1');
createButton(button1, '哈哈哈哈')
</script>
<div id="div1"></div>
<!--创建一个点击事件,调用之前创建的函数-->
<script>
window.document.body.onclick = function () {
var div1 = document.getElementById('div1')
createButton(div1, '笑一个吧~')
}
</script>
</body>
复制代码
JavaScript几乎可以添加删除修改所有的节点,但是它不能去动没有创建的节点,所以为了避免这个问题,我们要把JavaScript代码放到页面底部。
当JavaScript代码执行结束后,浏览器就退出了JavaScript的执行模式继续HTML到DOM的解析过程直到再次遇到脚本节点。重复以上过程。最后页面创建阶段就结束了。
并且在这个过程中所有创建的全局变量都能正常被其他脚本元素中的JavaScript代码访问到。原因在于全局对象window对象在整个页面的生存期当中。
三、事件处理
事件处理的重点在于一次只能执行一个代码片段,可以想象一下食堂一次只能一个人打饭。
由于一次只能执行一个代码片段,浏览器把触发的事件依次推到了一个事件队列中以这种方式来跟踪尚未处理的事件。
浏览器会检查事件队列头,如果没有找到就继续检查,如果找到了事件就取出该事件执行相应的事件处理器,重复执行。
此外放置事件的队列是独立在页面构建阶段和事件处理阶段以外的。
#异步
事件是异步的,代码的提前建立是为了保证之后的执行。
#注册事件处理器
- 一个方法是把函数赋给某些特殊属性
window.onload=function(){}
复制代码
- 使用内置方法 addEventListener
document.body.addEventListener("onclick",function(){})
复制代码
使用后者的优势在于可以注册尽可能多的事件,而前者只能注册一个事件处理器
#处理事件的流程
触发事件后,事件被推到事件队列中。
解决问题
- 浏览器是否会总是根据给定的html来渲染页面?
是的。但是不仅仅是THML,在页面构建过程中解析HTML成DOM和JavaScript代码的执行是交替进行的(这里不讲css)
- web 应用一次能处理多少个事件?
一次一个。由于JavaScipt基于一个单线程的执行模型。
- 为什么浏览器使用事件队列来处理事件?
因为一次只能执行一个,所以为了追踪触发的事件就使用了事件队列,并且队列是先进先出的页很公平。