title: JS執行順序
order: 1
文章目錄
- 一.概念
- 1.解釋型語言執行步驟
- 2.任務佇列event queue
- 3.🧡Event Loop
- 4.宏任務、微任務的執行順序
- 5.變數提升
- 5.作用域的深層次理解
- 6.堆(heap)和堆疊(stack)
- 7.內層的強參考和弱參考
- 二.執行問題
- 1.考察宏任務微任務
- 2.考察promise
- 3.考察閉包
- 4.考察作用域
- 5.考察預編譯
- 6.考察函式傳參和地址
- 三.定時器經典案例
一.概念
1.解釋型語言執行步驟
預編譯
掃描背景關系,掃描錯誤
函式和變數宣告的提升
解釋執行
一條一條執行代碼
2.任務佇列event queue
📢異步任務進入event queue任務佇列
所有任務可以分成兩種,一種是同步任務(synchronous),另一種是異步任務(asynchronous),同步任務指的是,在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務;異步任務指的是,不進入主執行緒、而進入"任務佇列"(task queue)的任務,只有"任務佇列"通知主執行緒,某個異步任務可以執行了,該任務才會進入主執行緒執行,
具體來說,異步執行的運行機制如下,(同步執行也是如此,因為它可以被視為沒有異步任務的異步執行,)
任務進入執行堆疊----同步任務還是異步任務----同步的進入主執行緒,異步的進入Event Table并注冊函式,當指定的事情>完成時,Event Table會將這個函式移入Event Queue,主執行緒內的任務執行完畢為空,會去Event Queue讀取對應的函式,進入主執行緒執行,上述程序會不斷重復,也就是常說的Event Loop(事件回圈)
📢只要主執行緒空了,就會去讀取"任務佇列",這就是JavaScript的運行機制,這個程序會不斷重復,
📢異步任務先進入event table,有結果后進去event queue注冊事件,同步任務在主執行緒執行完后執行事件佇列的事件
3.🧡Event Loop
主執行緒從"任務佇列"中讀取事件,這個程序是回圈不斷的,所以整個的這種運行機制又稱為Event Loop(事件回圈),
4.宏任務、微任務的執行順序
常見的宏任務:setTimeout, setInterval, setImmediate
常見的微任務:Promise.then
執行順序:先執行同步代碼,遇到異步宏任務則將異步宏任務放入宏任務佇列中,遇到異步微任務則將異步微任務放入微任務佇列中,當所有同步代碼執行完畢后,再將異步微任務從佇列中調入主執行緒執行,微任務執行完畢后再將異步宏任務從佇列中調入主執行緒執行,一直回圈直至所有任務執行完畢,
setTimeout(function(){
console.log('4');
});
new Promise(function(resolve){
console.log('1');
resolve();
}).then(function(){
console.log('3');
});
console.log('2');
5.變數提升
匿名函式宣告比變數函式宣告提身更高
var foo = function () {
console.log('2');
}
function foo() {
console.log('1');
}
foo();//2
5.作用域的深層次理解
當函式代碼執行的前期 會創建一個執行期背景關系內部物件 AO (作用域)
這個內部的物件是預編譯的時候創建出來的 因為當函式被呼叫的時候 會先進行預編譯
在全域代碼執行的前期會創建一個執行期的背景關系的物件 GO
函式作用域預編譯
-
創建ao物件
AO{} -
找形參和變數宣告 將變數和形參名 當作
AO物件的屬性名 值為undefined -
實參形參相統一
-
在函式體里面找函式宣告 值賦予函式體
全域作用域的預編譯
- 創建
GO物件 - 找變數宣告 將變數名作為
GO物件的屬性名 值為undefined - 找函式宣告 值賦予函式體
6.堆(heap)和堆疊(stack)
堆疊會自動分配記憶體空間,會自動釋放,堆動態分配的內層,大小不定也不會自動釋放
7.內層的強參考和弱參考
- 強參考:
map就是強參考、參考的那個物件置為null了,map應用的那個地址還存在 - 弱參考:
weakMap就是弱參考、被參考的物件會自動被垃圾回收
二.執行問題
1.考察宏任務微任務
console.log('1');
setTimeout(function(){
console.log('2');
new Promise(function(resolve){
console.log('3');
resolve();
}).then(function(){
console.log('4');
})
})
new Promise(function(resolve){
console.log('5');
resolve();
}).then(function(){
console.log('6');
})
setTimeout(function(){
console.log('7');
new Promise(function(resolve){
console.log('8');
resolve();
}).then(function(){
console.log('9');
})
})
//輸出1,5,6,2,3,4,7,8,9
2.考察promise
function fun2() {
console.log(1)
return Promise.reject('4')//沒有catch無法捕獲錯誤,阻止了后面執行
}
async function fun1(){
console.log(2)
await fun2()
console.log(3)
}
fun1()//列印1 2 報錯
3.考察閉包
function Foo() {
var i = 0;
return function () {
console.log(i++);
}
}
var f1 = Foo(),
f2 = Foo();
f1();//0、閉包延長作用域
f1();//1
f2();//0、匿名函式所以沒有指向同一個地址、指向兩個不同函式物件
4.考察作用域
(function(){
//"use strict"在輸出b的時候就會Uncaught ReferenceError: a is not defined
var a=b=5;//相等于var a=5;b=5
})();
console.log(b);//5
console.log(a);//Uncaught ReferenceError: a is not defined
5.考察預編譯
function fn(a, c) {
var a = 123
console.log(a)//123
console.log(c)//function c
function a() {}
if (false) {
var d = 678
}
console.log(d)//undefined
console.log(b)//undefined
var b = function () {}
console.log(b)///funb
function c() {}
console.log(c)//function c
}
fn(1,2)
6.考察函式傳參和地址
let obj = {name:'小明'}
function func(a) {
a.name="小花";
a={age:18};
}
func(obj);
console.log(obj)
三.定時器經典案例
最簡單的一種、沒有異步
for(var i=0;i<5;i++){//i++`相當于先賦值后`i+1
console.log(i)//立即列印了0,1,2,3,4
}
加上定時器、第二個引數可有可無
for (var i = 0; i < 5; ++i) {
setTimeout(()=>{
console.log(i)//立即列印出5個5
})
}
// js 運行環境為單執行緒,setTimeout 注冊的函式需要等到執行緒空閑時才能執行,此時 for 回圈已經結束,i 值為 5,又因為回圈中 setTimeout 接受的引數函式通過閉包訪問變數 i,所以 5 個定時輸出都是 5,
將 setTimeout 放在立即執行函式中、不傳引數i
for (var i = 0; i < 5; i++) {
(function () {
setTimeout(() => {
console.log(i)//隔了1s立即列印5個5
}, i*1000)
})()
}
傳引數i
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(() => {
console.log(i)//每秒遞增列印
}, i*1000)
})(i)
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/319800.html
標籤:其他
