BOM与DOM
- BOM:浏览器对象模型,用来设置浏览器的属性、行为,提供了独立于内容而与浏览器窗口进行交互的对象。描述了与浏览器进行交互的方法和接口,可以对浏览器窗口进行访问和操作。
- DOM:文档对象模型,用来设置文档中标签的属性,是针对XML的基于树的API。描述了处理网页内容的方法和接口,是HTML和XML的API,DOM把整个页面规划成由节点层级构成的文档。DOM本身是与语言无关的API,
<script>
标签
- 通过
<script>
标签可以直接将js代码嵌入网页。 <script>
标签 可以以指定加载外部脚本<script>
标签 设置 integrity属性,写入该外部脚本Hash前面,可以验证脚本一致性,防止攻击者篡改外部脚本,<script>
标签加载外部脚本时,浏览器会暂停页面渲染<script>
标签应该放在页面底部,避免外部脚本加载时间过长造成页面阻塞问题。<script>
标签 脚本执行的属性是按照在页面出现的顺序决定的。<script>
标签 defer属性告诉浏览器等DOM加载完成后在执行脚本,浏览器解析过程中 会并行下载带有<script>
标签 defer属性的脚本,同时继续解析HTML网页,浏览器完成解析HTML网页后,在执行下载的脚本。<script>
标签 async 属性,使用另一个进程下载脚本,下载时不会阻塞渲染 ,浏览器解析过程中解析过程中会并行下载带有<script>
标签asnc属性的脚本,同时继续解析HTML网页,当脚本下载完成后,浏览器粘贴解析HTML网页,开始执行下载的脚本,脚本执行完毕后,浏览器回复解析HTML网页。- 浏览器默认采用HTTP协议写在脚本,如果希望,根据页面本身的协议来决定加载协议,可以这样
<script src="//user.js"></script>
。
js虚拟机
- 即时编译(JIT),即字节码只在运行时编译,并将编译结果缓存。
- 有的js引擎省略字节码编译步骤,直接编译成机器码(chrome v8引擎),js虚拟机不完全基于字节码,有部分基于源码,利用JIT编译器直接将源码编译成机器码运行,就省略了字节码步骤。
- 字节码不能直接运行,而是运行在虚拟机上,一般虚拟机称为js引擎。
js 单线程模型
- js是单线程运行的(js可以修改DOM,涉及多个线程对DOM操作,会引发更为复杂的概念,为了避免复杂性,js被设计为单线程)。
- H5中,js被允许创建多个线程,但子线程收主线程控制,且不得操作DOM,
- 单线程的模型,当任务队列阻塞时,会使得浏览器失去响应,为此JS 设计者采用一种机制,将处于等待的任务挂起,先运行排在后面的任务,当挂起的等待任务有了结果,再回过头执行挂起的任务,这种机制被称之为Event Loop。
Event Loop 和消息队列
- Event Loop 是一种执行模型,指的是一种内部循环,用来一轮又一轮的处理消息队列中的消息,即执行对应的回调函数。也可以理解为动态更新的消息队列本身。
- 所有任务分为同步任务和异步任务,同步任务指的是js执行进程按照顺序执行,只有前者任务执行完毕,后者任务才可以执行。异步任务指的是,不进入js进程,而是进入任务队列(task queue)任务,只有任务队列通知主进程某个异步任务可以执行了,该任务才会进入js进程。
宏队列和微队列
- 宏队列(macrotask) tasks,一些异步任务的回调会依次进入macro task queue 等待调用。
setTimeout
setInterval
setImmediate (Node独有)
requestAnimationFrame (浏览器独有)
I/O
UI rendering (浏览器独有)
- 微队列 (microtask ) jobs,另一些异步任务的回调会依次进入 micro task queue,等待后续被调用,
process.nextTick (Node独有)
Promise
Object.observe
MutationObserver
- 宏队列一次从队列中取一个任务执行,然后再去微任务队列microtask queue中依次取出执行,当微任务队列执行完后,会执行UI redering(执行完所有微任务后,下一个宏任务之前)。
- 微任务队列全部执行完,再去宏任务队列中取第一个任务执行。
Stack Queue: [任务栈], //调用栈
Macrotask Queue: [宏任务的回调函数],//宏队列
Microtask Queue: [微任务的回调函数],// 微队列
console.log(1);
setTimeout(() => {
console.log(2);
// callback1 ( 1.入宏队列)
Promise.resolve().then(() => {
// callback4 ( 4.入微队列)
console.log(3)
});
});
new Promise((resolve, reject) => {
console.log(4)
// callback2 ( 2.入微队列)
resolve(5)
}).then((data) => {
console.log(data);
})
setTimeout(() => {
// callback3 ( 3.入宏队列)
console.log(6);
})
console.log(7);
// 全部执行script后,调用栈和宏队列、微队列情况
Stack Queue: [console]
Macrotask Queue: [callback1, callback3]
Microtask Queue: [callback2]
打印结果:
1
4
7
1.先执行微队列callback2 => 1475
Stack Queue: [callback2]
Macrotask Queue: [callback1, callback3]
Microtask Queue: []
2.接着执行第一个宏队列callback1 =>14752
Stack Queue: [callback1]
Macrotask Queue: [callback3]
Microtask Queue: []
3.接着微任务进入微队列 => 14752
Stack Queue: [promise]
Macrotask v: [callback3]
Microtask Queue: [callback4]
4.执行微队列callback4 => 147523
Stack Queue: [callback4]
Macrotask Queue: [callback3]
Microtask Queue: []
5.最后执行第二个宏队列 callback3 =>1475236
tack Queue: [callback3]
Macrotask Queue: []
Microtask Queue: []