本文共 1090 字,讀完只需 4 分鐘javascript
JavaScript 是函數式編程語言,做用域也是以函數爲單位,那麼,這些函數代碼塊是怎麼樣的順序進行的呢, JS 的可執行代碼又分爲 3 種,不一樣類型的代碼有不同的執行環境。本文就梳理有關 JS 執行上下文(execution context),也叫執行環境的知識。java
活動的執行代碼的上下文在語言底層邏輯上構成一個執行上下文棧(excution context stack),咱們知道,棧有兩個行爲,壓入棧和彈出棧,而且有後進先出
的特色, 最早進入的項會在棧底最後彈出。git
不一樣執行環境的有其相應的變量對象(Variable Object),某個執行環境的全部可執行代碼都執行完畢後,該環境中的變量對象和函數定義會被清除。github
函數代碼會在執行完後清除變量佔用的內存,全局代碼則會在關閉環境,好比關閉瀏覽器後清除。web
JS 中可執行的代碼可分爲三種類型:編程
全局代碼瀏覽器
在 web 瀏覽器中,全局執行環境是 window 對象,全部的全局變量和函數都是做爲 window 的屬性和方法存在。bash
全局代碼的執行上下文棧能夠表示爲:閉包
ECStack = [
globalContext
]
複製代碼
函數代碼app
當執行函數代碼時,函數代碼上下文被壓入到執行上下文棧中。函數代碼的執行環境中,有本身的內部的定義的變量和聲明。
function foo1() {
var name1 = "a";
console.log(name1)
}
function foo2(){
var name2 = "b";
console.log(name2)
}
foo1();
foo2();
複製代碼
上面的執行上下文能夠表示爲:
ECStack = [
globalContext
];
ECStack.push(<foo1> functionContext);
ECStack.pop();
ECStack.push(<foo2> functionContext);
ECStack.pop();
複製代碼
一個函數,可能有多個執行上下文,每一個函數的調用都會產生新的上下文。
function foo() {
...
}
foo("a");
foo("b");
foo("c");
複製代碼
eval 代碼
eval
關鍵字接受一個字符串做爲參數,並將其做爲書寫文字上下文的代碼。
function foo(str, a) {
eval( str ); // 聲明一個新變量
console.log(a, b);
}
var b = 123;
foo("var b = 456 ", 123) // 123, 456
複製代碼
以上代碼引用《你不知道的 JavaScript》的例子,eval 函數中的字符串,被當作可執行代碼,最後在 foo 函數中聲明瞭 b 變量,並覆蓋了 foo 函數外部的 b 變量。
eval 函數和 JS 的詞法做用域的行爲,會形成變量環境和執行上下文的混亂,儘可能別使用它。
前面其實已經提到不少關於執行上下文棧的內容,簡而言之,JS 在遇到全局代碼,函數代碼,eval 代碼時,會建立相應的執行上下文,不一樣的上下文,有其變量對象(variable object: VO)和做用域。
看這一段代碼:
function func3() {
console.log('fun3')
}
function func2() {
func3();
}
function func1() {
func2();
}
func1();
複製代碼
以上代碼引用自冴羽的github,代碼順序用執行上下文棧來表示就是:
ECStack.push(globalContext);
// 調用 func1(), 進入 func1 執行環境
ECStack.push(<func1> functionContext);
// func1中調用了 func2,進入 func2 的執行上下文
ECStack.push(<func2> functionContext);
// func2 調用 func3, 進入 func3 的執行上下文
ECStack.push(<func3> functionContext);
// func3 執行完畢,退出 func3 執行環境
ECStack.pop();
// func2 執行完畢,退出 func2 執行環境
ECStack.pop();
// func1 執行完畢,退出 func1 執行環境
ECStack.pop();
複製代碼
函數代碼執行完畢後,執行上下文棧底只剩下全局代碼上下文 globalContext, 直到關閉瀏覽器纔會完全清空執行上下文棧。
代碼在執行時會建立由不一樣做用域構成的做用域鏈
,做用域鏈
保證 JS 中的變量和函數都可以有序地訪問和執行。
若是是函數代碼,則將其 活動對象
做爲變量對象,活動對象最開始時只包含一個對象,即 arguments
對象。
後面的文章會介紹變量對象(VO)和做用域鏈的具體內容,敬請期待吧。
博客內容源自我的公衆號,專一分享原創文章,歡迎關注。