抽象概念铲掐,簡而言之js是在執(zhí)行上下文中運行的窍箍。
類型 3種
全局執(zhí)行上下文
這是默認或者說基礎的上下文,任何不在函數(shù)內部的代碼都在全局上下文中哩照。它會執(zhí)行兩件事:創(chuàng)建一個全局的 window 對象(瀏覽器的情況下)挺物,并且設置 this 的值等于這個全局對象。一個程序中只會有一個全局執(zhí)行上下文飘弧。
函數(shù)執(zhí)行上下文
每當一個函數(shù)被調用時, 都會為該函數(shù)創(chuàng)建一個新的上下文识藤。每個函數(shù)都有它自己的執(zhí)行上下文,不過是在函數(shù)被調用時創(chuàng)建的次伶。函數(shù)上下文可以有任意多個蹋岩。每當一個新的執(zhí)行上下文被創(chuàng)建,它會按定義的順序(將在后文討論)執(zhí)行一系列步驟学少。
Eval 函數(shù)執(zhí)行上下文
執(zhí)行在 eval 函數(shù)內部的代碼也會有它屬于自己的執(zhí)行上下文,但由于 JavaScript 開發(fā)者并不經常使用 eval秧骑,所以在這里我不會討論它版确。
執(zhí)行棧
是一種LIFO(后進先出)的數(shù)據(jù)結構扣囊, 用來儲存執(zhí)行上下文
當javascript引擎檢測到腳本時,就會創(chuàng)建一個全局執(zhí)行上下文并且將其壓入到執(zhí)行棧底部(最先被創(chuàng)建的)绒疗。當有函數(shù)被調用時會生成一個函數(shù)執(zhí)行上下文侵歇,然后被壓入執(zhí)行棧的頂部。
執(zhí)行引擎會先執(zhí)行棧頂?shù)暮瘮?shù)執(zhí)行上下文吓蘑,當函數(shù)執(zhí)行完畢會被彈出惕虑,然后執(zhí)行下一個執(zhí)行期上下文。
創(chuàng)建執(zhí)行期上下文
在javascript代碼執(zhí)行前會創(chuàng)建執(zhí)行上下文磨镶。創(chuàng)建會經歷三件事
1. this的綁定
2. 詞法環(huán)境組件創(chuàng)建
3. 變量環(huán)境組件創(chuàng)建
所以會有:
ExecutionContext = {
ThisBinding = < this value > ,
LexicalEnvironment = {
...
},
VariableEnvironment = {
...
},
}
this綁定
被誰調用this就指向誰溃蔫,如果沒被調用就指向全局
let foo = {
baz: function() {
console.log(this);
}
}
foo.baz(); // 'this' 引用 'foo', 因為 'baz' 被對象 'foo' 調用
let bar = foo.baz;
bar(); // 'this' 指向全局 window 對象,因為沒有指定引用對象
詞法環(huán)境和變量環(huán)境
在 ES6 中琳猫,詞法環(huán)境和變量環(huán)境的一個不同就是前者被用來存儲函數(shù)聲明和變量(let 和 const)綁定伟叛,而后者只用來存儲 var 變量綁定。
在初始化的時候let 和 const 定義的變量并不會關聯(lián)任何值脐嫂,但 var 定義的變量被設成了 undefined统刮。
這是因為在創(chuàng)建階段時,引擎檢查代碼找出變量和函數(shù)聲明账千,雖然函數(shù)聲明完全存儲在環(huán)境中侥蒙,但是變量最初設置為 undefined(var 情況下),或者未初始化(let 和 const 情況下)匀奏。
這就是為什么你可以在聲明之前訪問 var 定義的變量(雖然是 undefined)鞭衩,但是在聲明之前訪問 let 和 const 的變量會得到一個引用錯誤。