Chrome 浏览器
js 代码在浏览器中是如何被执行的呢?
1.在浏览器输入一个网站链接,会经过dns 域名解析转换成IP 地址,ip 地址一般是服务器地址;
2.进入服务器地址之后,服务器一般会返回index.html;
3.浏览器从index.html解析到的css 文件和 js 文件都会从服务器下载下来;
浏览器内核
浏览器内核会将 html、css、js 解析,渲染成网页
- Geoko:早期,现在很少了
- Trident:edge 之前使用,现在 edge 已经转向 Blink 了
- Webkit: 苹果开发,之前谷歌浏览器也在使用
- Blink:Webkit 的一个分支,谷歌开发,目前应用于谷歌浏览器、Edge、Opera
浏览器内核也会影响兼容性
实际上浏览器内核就是浏览器的排版引擎,也叫浏览器引擎、页面渲染引擎
浏览器渲染过程
在DOM 变成 DOM 树这个过程,html 解析的时候遇到 js 标签,应该怎么办呢?
会停止解析 html,而去加载和执行 js 代码;
那 js 代码由谁来执行呢?;
js 引擎
js 引擎
为什么需要 js 引擎呢?
js是一门高级编程语言;
高级编程语言都是需要转化成机器指令来执行的;
js 代码无论是交给浏览器和node执行,最后都是需要被cpu执行的;
但是cpu只认识自己的指令集,也就是机器指令;
所以需要js 引擎将js 代码转换成cpu 指令;
那有哪些常见的 js 引擎呢?
- spiderMonkey:第一款 js 引擎
- Chakra
- JavaScriptCore:webkit 中的 js 引擎
- v8:谷歌开发的强大 js 引擎,(c++编写)
- ...
浏览器内核和 js 引擎的关系
以 webkit 为例,它由两部分组成
- webcore:负责 html 解析、布局、渲染等工作
- JavaScriptcore:解析执行 js 代码
js 代码生成的抽象语法树网站:astexplorer.net
v8 引擎原理
js 代码-->ast(抽象语法树)-->字节码-->机器指令
其中字节码的存在是为了跨平台和收集热函数;
什么叫热函数?执行次数比较多的函数;
将执行次数多的函数直接转换成机器指令放到字节码里,就不用每次都是字节码变机器指令,提升性能;
预解析
并不是所有就是代码一开始就会被执行,如果对所有的 js 代码进行解析会影响网页的运行效率
v8 引擎实现了 Lazy Parsing延迟解析的方案,它的作用是将不必要的函数进行预解析;
在也就是解析暂时需要的内容,而对函数的全量解析是函数被调用时才会进行;
事件循环
进程和线程
进程
启动一个应用程序,就会默认启动一个进程(也可能多个)
线程
每一个进程中,至少一个线程来执行程序中的代码,这个线程被称之为主线程
js 的线程
- js 是单线程的,他的进程容器是:浏览器或 node
- 同一时刻只能做一件事
- 如果这件事非常耗时,意味着当前的线程就会被阻塞
- 所以耗时的操作,并不会放在 js 线程上(放在其它线程)
- 比如网络请求、定时器
浏览器的事件循环
有三个角色:
- js 线程
- 其它线程
- 事件队列
- js 线程执行代码
- 当发现耗时操作时
- 会将这操作(会有回调的函数)交给其它线程处理
- 当其它线程处理完,会将回调函数放到事件队列中
- js 线程会定时地来事件队列执行那些回调函数
这三个角色形成一个闭环,不停地进行这些操作,称之为事件循环
事件队列
分 2 种:
- 微任务队列(microtask queue)
- 宏任务队列(macro queue)
微任务
哪些回调放微任务呢?
- queueMicrotask
- Promise.then()中的回调函数
宏任务
哪些回调放宏任务呢?
- 定时器
- ajax
- DOM 监听
- UI Rendering
执行顺序
原则:
在执行任何宏任务之前,都需要保证微任务队列已经被清空
node 事件循环
node 有个核心的库 libuv(c 语言),专注于文件 IO,使得 js 也能进行服务器开发
与浏览器大同小异
阶段
一次完整的时间循环分很多阶段:
- 定时器
- 待定回调
- idle,prepare
- 轮询
- 检测
- 关闭的回调函数
事件队列
node 的事件队列分复杂一点
宏任务
- 定时器
- IO 事件
- close 事件
微任务
- promise 的 then 回调
- process.nexTick(微任务中优先)
- queueMicrotask
事件监听
监听方式
- script 中
- 通过元素的 on
- 通过 EventTarget 中的 addEventListener
元素的 on
<div class="box" onclick="console.log(123)"></div>
<script src="./test.js"></script>
这种现在基本不在使用~
script 中
<div class="box"></div>
<script src="./test.js"></script>
const divE1 = document.querySelector(".box");
divE1.onclick = function () {
console.log(123);
};
缺点
不能重复,不能多个函数对其响应
当有多个相同事件,后面会覆盖掉前面
addEventListener
可以多个响应函数
目前用的比较多就是这种方式
<div class="box"></div>
<script src="./test.js"></script>
const divE1 = document.querySelector(".box");
divE1.onclick = function () {
console.log(123);
};
事件流
为什么会产生事件流呢?
当我们在浏览器上对着一个元素点击时,你点击的不仅仅是这元素本身
还有可能点击了其它元素,因为元素之间是可以层叠的~
事件冒泡
事件(如点击)从最内层往外依次传递的顺序,称事件冒泡
<div class="box">
<span></span>
</div>
<script src="./test.js"></script>
const divE1 = document.querySelector(".box");
const spanE1 = document.querySelector("span");
divE1.addEventListener("click", () => {
console.log("div被点击");
});
spanE1.addEventListener("click", () => {
console.log("span被点击");
});
document.body.addEventListener("click", () => {
console.log("body被点击");
});
// 点击span时,打印
// span被点击
// div被点击
// body被点击
事件捕获
与事件冒泡相反,事件从最外层往内依次传递 ,称事件捕获
为什么会产生两种不同处理流呢?
浏览器早期开发时,ie 和 Netscape 公司都发现了这个问题,但是他们采取了相反的策略:
- ie:事件冒泡
- Netscape:事件捕获
监听事件捕获
如何去监听事件捕获呢?
addEventListener 第 3 个参数,true,默认是 false,事件冒泡
<div class="box">
<span></span>
</div>
<script src="./test.js"></script>
const divE1 = document.querySelector(".box");
const spanE1 = document.querySelector("span");
divE1.addEventListener(
"click",
() => {
console.log("div被点击");
},
true
);
spanE1.addEventListener(
"click",
() => {
console.log("span被点击");
},
true
);
document.body.addEventListener(
"click",
() => {
console.log("body被点击");
},
true
);
// 点击span时,打印
// body被点击
// div被点击
// span被点击
要是同时有事件捕获和事件冒泡,事件传递顺序是:先事件捕获再事件冒泡
事件对象 event
当产生一个事件时,和事件相关的信息也伴随着产生了,浏览器将这些信息放在一个对象里,并且传给了对应的处理函数
<div class="box"></div>
<script src="./test.js"></script>
const divE1 = document.querySelector(".box");
divE1.addEventListener("click", (event) => {
console.log(event);
}); // PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0, …}
常见属性
- 事件类型 type
- 事件源 target
- 事件发生位置 offsetX/Y
常见方法
preventDefault | 阻止默认行为 | 如 a 元素的默认跳转 |
---|---|---|
stopPropagation | 阻止事件进一步传递 |
BOM-DOM
BOM
浏览器对象模型
Browser Object Model,BOM
BOM,连接js 和浏览器窗口的桥梁。
主要的对象模型
- window
- location
- history
- document
window
全局属性、方法以及控制浏览器窗口相关的属性,方法