最近在学前端基础,今天梳理一下javascript
的运行机制
只是简单理解,完全没入门那种~
首先必须记录的事Javascript
是单线程的,目的是简化和用户的交互逻辑,从而不会出现严重的并发问题。
但是js
脚本可以创建多个线程,但是创建的子线程受到主线程控制,同时不能操作DOM
。
相关进程和线程
在浏览器中加载页面主要用到4个进程:
- 主进程:(
Browser
进程)负责页面展示和资源下载等 GPU
进程:负责3D
图示绘图- 第三方插件进程:负责第三方插件的处理
- 渲染进程:(
Render
进程),负责js
执行,页面渲染等功能
其中渲染进程中又包括:
GUI
渲染进程Js
引擎线程- 事件循环线程
- 定时器线程
http
异步线程- …
这里主要看渲染进程的相关线程
GUI渲染线程
主要处理的事情包括:
- 首先浏览器会解析
html
代码(实际上html代码本质是字符串)转化为浏览器认识的节点,生成DOM
树,也就是DOM Tree
- 然后解析
css
,生成CSSOM
(CSS
规则树) - 把
DOM Tree
和CSSOM
结合,生成Rendering Tree
(渲染树)
注意其中存在重绘和回流:
如果修改了一些元素的颜色或者背景色,页面就会重绘(
Repaint
),如果修改元素的尺寸,页面就会回流(Reflow
),当页面需要Repaing
和Reflow
时GUI多会执行,进行页面绘制。
Js引擎线程
负责解析和执行js
代码,浏览器同时只能有一个JS
引擎线程在运行JS
程序,所以js
是单线程运行的。
Js线程会阻塞渲染线程,因此<script>
标签和其他DOM标签是顺序执行的,一旦执行到<script>
标签就会立即执行,所以一般我们把<script>
标签放在·body
的最后
事件循环线程
用来控制事件的循环,维护和管理一个任务队列(task queue
)。
下面会讲到关于事件队列的用法
定时器线程
单独用来计时,计时完成将定时器执行的操作添加到事件任务队列尾。
异步请求线程
执行到一个http
异步请求时,便把异步请求事件添加到异步请求线程,等待响应;并把回调函数添加到事件队列,等待js引擎线程执行
Eventloop
Eventloop
其实是Js
的执行机制,表示了线程之间的协作关系
因为js是单线程,因此任务的执行只能顺序执行,为了将耗时的任务分离出来,在js中实现了两种任务类型,同步任务和异步任务。
- 所有同步任务在主线程上执行,形成执行栈
- 任务队列(task queue,就是由事件循环线程维护)负责存放异步任务的运行结果
- 执行栈的同步任务执行完成后会读取任务队列,将对应的异步任务加入执行栈执行
- 上述操作不断循环往复
诸如此图
注意一句话:
javascript
的执行和运行有很大的区别,javascript
在不同的环境下,比如node
,浏览器,Ringo
等等,执行方式是不同的。而运行大多指javascript
解析引擎,是统一的。
定时器
定时器是一个相当特殊的存在,任务队列中不仅可以放普通事件,也可以放定时事件。
主要由setTimeout()
和setInterval()
两个函数完成
值得注意的是
定时器不会完全按照规定时间执行
- W3C标准规定
setTimeout
中最小的时间周期是4毫秒,凡是低于4ms的时间间隔都按照4ms来处理。因此代码不会完全精确按照所定时间执行 - 主线程执行到定时器后,会把定时任务交给定时器线程执行,等到时间了,定时器所要执行的操作会放到任务队列末尾,因此不一定立即执行
- W3C标准规定
setInterval
存在累计效应:如果定时器里面的代码执行所需的时间大于定时器的执行周期,会导致有些事件丢失可以使用
setTimeout()
代替setInterval()
1
2
3
4
5
6var say = function() {
setTimeout(say, 1000)
console.log('hello world')
}
setTimeout(say, 1000)
剩下的以后再总结…