背景:在面試中經常會遇到函數和變量提升,作用域等問題,如果想深入理解其原理,那麼首先要弄清楚函數執行上下文和執行上下文棧這兩個概念。
再次之前先介紹下棧的數據結構:
總結起來一句話:新的數據從棧頂壓入,彈出數據也是從棧頂進行彈出,也就是我們所說的彈夾原理。
1.執行上下文(Excution Context)
執行上下文可以理解爲當前代碼的執行環境,它會形成一個作用域。JavaScript中的運行環境大概包括三種情況。
全局環境:JavaScript代碼運行起來會首先進入該環境
函數環境:當函數被調用執行時,會進入當前函數中執行代碼
eval(不建議使用,可忽略)
2.執行環境棧(執行上下文棧Excution Context Stack)
JavaScript執行在單線程上,所以的代碼都是排隊執行.棧底永遠都是全局上下文,而棧頂就是當前正在執行的上下文。當一開始瀏覽
器執行全局的代碼時,首先創建唯一的一個全局的執行上下文,並將其壓入執行棧的頂部(在瀏覽器關閉的時候出棧).當沒進入一個函數
的執行就會創建新的函數執行上下文,並相應的壓入執行棧的頂部.當前函數完成之後,當前函數的執行上下文從棧頂出棧,等待垃圾回
收。
3.執行上下文的生命週期
總的生命週期:創建-->執行-->出棧等待銷燬
A 創建變量對象:首先初始化函數的參數arguments,初始化函數聲明,初始化變量(undefined)。函數的優先級要高於變量,如果變
量和函數名重名,變量會被忽略。
a 創建arguments對象,檢查上下文,初始化參數名稱和值並創建引用的複製。
b 掃描上下文的函數聲明(而非函數表達式)
1.每找到一個函數,在變量對象variableObject上創建一個屬性-----切確的說是函數的名字---屬性值就是指向該函數在內存中的地址的一個引用。
2.如果上述函數名已經存在於variableObject下,那麼對應的屬性值會被新的引用所覆蓋。
c 掃描上下文的變量聲明
1.每找到一個變量聲明,就會在變量對象上創建一個屬性---就是變量名字,並且將變量的值初始化爲undefined
d.確定上下文內部this的指向
B 創建作用域鏈
執行階段:
執行變量賦值,代碼執行
回收階段:
執行上下文棧等待垃圾回收機制回收上下文
案例:(下面代碼用來說明執行上下文棧的工作原理) //變量聲明
var a1 = 9,
a2 = 8,
a3 = "sss",
b1 = {name:"xixi"};
//函數調用
a1 = f1(a1,a2);
//函數聲明
function f1(a,b){
//f1函數的執行上下文
/*
1.掃描參數: a = 9 b = 8
2.掃描函數聲明 f2 = function(){}
3.掃描變量聲明 t = undefined , m = undefined , i = undefined
*/
var t = 0,
m = 10;
for(var i=0;i<a;i++){
console.log(i);
}
function f2(){
console.log("f2");
}
return a+b;
}
環境棧示意圖:
以上是關於執行上下文執行環境棧的說明,關於作用域的問題可以參考本博客高級JavaScript部分的文章。