一、执行栈
JS运行的环境称之为宿主环境。
执行栈:call stack,一个数据结构,用于存放各种函数的执行环境,每一个函数执行之前,它的相关信息会加入到执行栈。函数调用之前,创建执行环境,然后加入到执行栈;函数调用之后,销毁执行环境。(单线程不容易冲突,但是效率低)
如下函数:
functiona(){
console.log("a")b();}functionb(){
console.log("b");c();}functionc(){
console.log("c")}
console.log("global");a();
- 函数a执行,将a放入执行期上下文
- log(‘a’)执行,将log(‘a’)放入
- log(‘a’)执行完毕,将log(‘a’)移除
- 函数b执行,将b放入执行期上下文
…
n-1. 函数b执行完毕,将b移除
n. 函数a执行完毕,将a移除
对于递归函数:
functiongetFeibo(n){if(n===1|| n===2){return1;}returngetFeibo(n-1)+getFeibo(n-2);};
console.log(getFeibo(4));
- log(getFeibo(4))执行,将log(getFeibo(4))放入
- getFeibo(4)执行,将
getFeibo(3)
+getFeibo(2)
放入getFeibo(3)
执行,将getFeibo(2)+getFeibo(1)放入
…- getFeibo(2)+getFeibo(1)执行完毕,将
getFeibo(3)
移除getFeibo(2)
执行,将getFeibo(2)
放入getFeibo(2)
执行完毕,将getFeibo(2)
移除- getFeibo(4)执行完毕,将getFeibo(4)移除
- log(getFeibo(4))执行完毕,将log(getFeibo(4))移除
JS引擎永远执行的是执行栈的最顶部。
二、异步函数
异步函数:某些函数不会立即执行,需要等到某个时机到达后才会执行,这样的函数称之为异步函数。比如事件处理函数。异步函数的执行时机,会被宿主环境控制。
浏览器宿主环境中包含5个线程:
- JS引擎:负责执行执行栈的最顶部代码
- GUI线程:负责渲染页面 (js线程与GUI线程会相互等待,一者工作一者等待)
- 事件监听线程:负责监听各种事件
- 计时线程:负责计时
- 网络线程:负责网络通信
当上面的线程发生了某些事请,如果该线程发现,这件事情有处理程序,它会将该处理程序加入一个叫做事件队列
的内存。当JS引擎发现,执行栈中已经没有了任何内容后,会将事件队列中的第一个函数加入到执行栈中执行。
JS引擎对事件队列的取出执行方式,以及与宿主环境的配合,称之为事件循环。
例1:
<div><button id="btn">点击</button></div><script>
document.getElementById("btn").onclick=functionA(){//这里A只是后面好讲解
console.log("按钮被点击了");};</script>
- 全局上下文
- getElementById执行,getElementById放入
- getElementById执行完毕,getElementById移除
a.浏览器宿主 发现点击事件,将 点击函数A 放入事件队列- 全局执行完毕,执行栈中已经没有了任何内容后
b. 将 点击函数A 加入到执行栈中执行
例2:
console.log("a")setTimeout(()=>{
console.log("b")},0);for(let i=0; i<1000; i++){
console.log("c")};
- 全局上下文
- log(‘a’)执行,放入
- log(‘a’)完毕,移除
a.浏览器宿主 发现计时线程,将 setTimeout 放入事件队列- for循环事件执行,放入
- for循环事件完毕,移除
- 全局执行完毕,执行栈中已经没有了任何内容后
b. 将 计时线程 加入到执行栈中执行
- 宏任务(队列):macroTask,计时器结束的回调、事件回调、http回调等等绝大部分异步函数进入宏队列
- 微任务(队列):MutationObserver,Promise产生的回调进入微队列
当执行栈执行完毕后,会先在微队列中找需要执行的事件
let count=1;const ul= document.getElementById("container");
document.getElementById("btn").onclick=functionA(){setTimeout(functionC(){//宏队列
console.log("添加了一个li")},0);var li= document.createElement("li")
li.innerText= count++;
ul.appendChild(li);};//监听ul,该函数就会进入微队列const observer=newMutationObserver(()=>{//当监听的dom元素发生变化时运行的回调函数
console.log("ul元素发生了变化")});//监听ul
observer.observe(ul,{
attributes:true,//监听属性的变化
childList:true,//监听子元素的变化
subtree:true//监听子树的变化(子元素的后代)});//不想监听后可以取消监听// observer.disconnect();
let count=1;const ul= document.getElementById("container");
document.getElementById("btn").onclick=functionA(){var li= document.createElement("li")
li.innerText= count++;
ul.appendChild(li);
console.log("添加了一个li");};//监听ul,该函数就会进入微队列const observer=newMutationObserver(()=>{//当监听的dom元素发生变化时运行的回调函数
console.log("ul元素发生了变化")});//监听ul
observer.observe(ul,{
attributes:true,//监听属性的变化
childList:true,//监听子元素的变化
subtree:true//监听子树的变化(子元素的后代)});//不想监听后可以取消监听// observer.disconnect();