iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 6
0
Modern Web

教練我想學 JavaScript 系列 第 6

Day 6 函數呼叫與執行堆及變數環境

函數呼叫(Function Invocation)與執行堆(Execution Stack)

全域執行環境被創造並執行時是放到執行堆去執行的,

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖

函數需要呼叫才會執行,函數在創造階段時就已經在記憶體中,函數被呼叫時會將函數的執行環境放到執行堆最上方(即是正在執行的程式,逐步、同步的),當執行完後函數的執行環境會離開執行堆最上方(回到全域執行環境)。

範例如下:

function b() {

}

function a() {
b();
}

a();

當函數 a 被呼叫,會產生一個新的執行環境並被放進執行堆裡,

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖

當函數 b 被呼叫,也會產生一個新的執行環境並被放進執行堆裡,會在執行堆最上方,表示正在執行的程式,

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖

再來看另外一個例子:

function a() {
b();
var c;
}
function b() {
var d;
}

a();
var d;

由於程式碼是逐行被解析的,
首先函數 a 會被呼叫,
因此函數 a 的執行環境會進入執行堆,
接著,它會變成目前執行的程式,

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖

最後一行的 var d 在函數 a 下一行 所以還不會被呼叫,
因為 JavaScript 是同步的(一次執行一行),
現在執行的程式就是目前執行堆裡最上方的執行環境,
所以當函數 a 的執行幻境在執行堆最上面時,它會逐行執行,
一路下來之後,呼叫函數 b ,
函數 b 會創造出自己的執行環境, 然後進入執行堆最上方,

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖

所以開始逐行執行,
呼叫函數 b,會在記憶體空間中設置變數 d 的初始值(undefined),

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖

接著回去執行函數 a ,替變數 c 設置在記憶體空間的位置,

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖

接著函數 b 的執行環境離開執行堆,
返回函數 a 的執行環境,現在函數 a 的執行環境在執行堆最上方(正在執行的程式),

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖

執行剛剛在執行環境,
還沒執行的那一行,
在記憶體空間設置 c 的位置,

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖

當函數 a 執行完畢,函數 a的執行環境離開執行堆,
執行全域執行環境中下一行還沒被執行的程式,
在全域執行環境中,在記憶體空間中設置變數 d 的初始值(undefined).

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 14 影片截圖

知道如何運作了嗎?

每當函數被呼叫,
一個新的執行環境就被創造給函數,
this 變數被創造給那個函數,
裡面的變數在創造階段就已經建立了,
然後程式碼會被逐行執行,
但每當函數被呼叫,即使是被自己呼叫,
一個新的執行環境就會進入執行堆,
然後當執行完畢後,離開執行堆,
無論最上面是什麼,那個就是正在執行的程式,
逐行的、同步的,
這個觀念是非常重要的。

函數環境(Function Context)與變數環境(Variable Environment)

每個函數環境(執行環境)都會有自己的變數環境,
舉個例子來說,
程式碼如下:

function b() {
var myVar;
console.log(myVar);
}
function a() {
var myVar = 2;
console.log(myVar);
b();
}
var myVar = 1;
console.log(myVar);
a();
console.log(myVar); // 呼叫 a 函數後再次看 myVar 還是為1,因為這在全域執行環境執行

全域執行環境被創造,myVar 被放進記憶體中,

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 15 影片截圖

當呼叫 a 函數時,a 函數的執行環境被創造,
當它的執行環境被創造,myVar 會被放到執行環境的變數環境,
每個執行環境都有自己的變數環境,a 函數的執行環境的變數環境被創造 myVar 的值為2(在執行到 myVar = 2那行程式時),

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 15 影片截圖

接著在呼叫 b 函數,一個執行環境和變數環境被創造,

圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 2 節講座 15 影片截圖

這個變數也有自己的記憶體指向位置,因為他還沒有被設定為任何值,myVar 在記憶體中會指向 undefined
這和 scope 有關,這表示我們可以在哪裡看到變數,這裡的每個變數都被定義在自己的執行環境。

我們透過 Console 來看變數的值是不是跟我們想的一樣,

function b() {
  var myVar;
  console.log(myVar);
}
function a() {
  var myVar = 2;
  console.log(myVar);
  b();
}

var myVar = 1;
console.log(myVar);
a();

在 Chrome 中 Console 結果:

如果我們在執行完 a 函數後在將 myVar 的值透過 console.log 顯示出來,
程式碼如下:

function b() {
  var myVar;
  console.log(myVar);
}
function a() {
  var myVar = 2;
  console.log(myVar);
  b();
}

var myVar = 1;
console.log(myVar);
a();
console.log(myVar);

myVar 的值會從 1 被修改嗎?

我們可以打開 Chrome 看一下 Console 中的結果:

答案是不會!
因為函數 a 的執行環境已經離開執行堆,
回到全域執行環境,
執行堆裡只剩全域執行環境,
在全域執行環境的變數環境中 myVar 的值被設定為 1,

每個執行環境都有自己的變數環境,不會互相干擾。


上一篇
Day 5 執行環境的執行階段
下一篇
Day 7 範圍鏈
系列文
教練我想學 JavaScript 30

尚未有邦友留言

立即登入留言