更新:謝謝你們的支持,最近折騰了一個博客官網出來,方便你們系統閱讀,後續會有更多內容和更多優化,猛戳這裏查看前端
------ 如下是正文 ------webpack
本期的主題是調用堆棧,本計劃一共28期,每期重點攻克一個面試重難點,若是你還不瞭解本進階計劃,文末點擊查看所有文章。git
若是以爲本系列不錯,歡迎點贊、評論、轉發,您的支持就是我堅持的最大動力。github
JS是單線程的語言,執行順序確定是順序執行,可是JS 引擎並非一行一行地分析和執行程序,而是一段一段地分析執行,會先進行編譯階段而後纔是執行階段。web
翠花,上代碼面試
例子一:變量提高算法
foo; // undefined
var foo = function () {
console.log('foo1');
}
foo(); // foo1,foo賦值
var foo = function () {
console.log('foo2');
}
foo(); // foo2,foo從新賦值
複製代碼
例子二:函數提高跨域
foo(); // foo2
function foo() {
console.log('foo1');
}
foo(); // foo2
function foo() {
console.log('foo2');
}
foo(); // foo2
複製代碼
例子三:聲明優先級,函數 > 變量數組
foo(); // foo2
var foo = function() {
console.log('foo1');
}
foo(); // foo1,foo從新賦值
function foo() {
console.log('foo2');
}
foo(); // foo1
複製代碼
上面三個例子中,第一個例子是變量提高,第二個例子是函數提高,第三個例子是函數聲明優先級高於變量聲明。瀏覽器
須要注意的是同一做用域下存在多個同名函數聲明,後面的會替換前面的函數聲明。
執行上下文總共有三種類型
this
指向這個全局對象。eval
函數中的代碼,不多用並且不建議使用。這部份內容在【進階1-1期】中詳細介紹了,點擊查看【進階1-1期】理解JavaScript 中的執行上下文和執行棧
由於JS引擎建立了不少的執行上下文,因此JS引擎建立了執行上下文棧(Execution context stack,ECS)來管理執行上下文。
當 JavaScript 初始化的時候會向執行上下文棧壓入一個全局執行上下文,咱們用 globalContext 表示它,而且只有當整個應用程序結束的時候,執行棧纔會被清空,因此程序結束以前, 執行棧最底部永遠有個 globalContext。
ECStack = [ // 使用數組模擬棧
globalContext
];
複製代碼
具體執行過程以下圖所示,這部份內容在【進階1-1期】中詳細介紹了,點擊查看【進階1-1期】理解JavaScript 中的執行上下文和執行棧
有以下兩段代碼,執行的結果是同樣的,可是兩段代碼究竟有什麼不一樣?
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
複製代碼
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
複製代碼
答案是 執行上下文棧的變化不同。
第一段代碼:
ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();
複製代碼
第二段代碼:
ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();
複製代碼
在函數上下文中,用活動對象(activation object, AO)來表示變量對象。
活動對象和變量對象的區別在於
調用函數時,會爲其建立一個Arguments對象,並自動初始化局部變量arguments,指代該Arguments對象。全部做爲參數傳入的值都會成爲Arguments對象的數組元素。
執行上下文的代碼會分紅兩個階段進行處理
一、進入執行上下文
二、代碼執行
很明顯,這個時候尚未執行代碼
此時的變量對象會包括(以下順序初始化):
上代碼就直觀了
function foo(a) {
var b = 2;
function c() {}
var d = function() {};
b = 3;
}
foo(1);
複製代碼
對於上面的代碼,這個時候的AO是
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: undefined,
c: reference to function c(){},
d: undefined
}
複製代碼
形參arguments這時候已經有賦值了,可是變量仍是undefined,只是初始化的值
這個階段會順序執行代碼,修改變量對象的值,執行完成後AO以下
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: 3,
c: reference to function c(){},
d: reference to FunctionExpression "d"
}
複製代碼
總結以下:
一、全局上下文的變量對象初始化是全局對象
二、函數上下文的變量對象初始化只包括 Arguments 對象
三、在進入執行上下文時會給變量對象添加形參、函數聲明、變量聲明等初始的屬性值
四、在代碼執行階段,會再次修改變量對象的屬性值
進階系列文章彙總:github.com/yygmind/blo…,內有優質前端資料,以爲不錯點個star。
我是木易楊,網易高級前端工程師,跟着我每週重點攻克一個前端面試重難點。接下來讓我帶你走進高級前端的世界,在進階的路上,共勉!