JavaScript Weird Part (4) - 創造與提昇

Javascript 執行時,必經的創造執行環境的階段,究竟底層發生什麼變化?在下列的範例中,假如我們把呼叫移到程式碼最上方,移到函式之前:

1
2
3
4
5
6
b();
console.log(a);
var a = "hello world";
function b() {
console.log("called b !");
}

在多數程式語言裡面,這,因為還沒宣告函式就先呼叫,所以呼叫是無效的。但 JavaScript 不僅執行函式,而且沒有錯誤。

Called b! 正常印出來,代表函式 b 正常執行,但是 undefined 呢?

Javascript 程式碼在執時,不是直接被執行,而是被解析器轉換成電腦看得懂的東西。當解析器執行你的程式碼時,它會知道你在什麼位置創造變數與函數。

這個階段稱為創造 (creation),而創造階段裡面,直譯器在記憶體裡設定變數與函數的這個步驟稱為「提昇」(hoisting) 。

它不是真的把程式碼移到最上面。在逐行執行程式碼之前,JavaScript 已經替變數與函數,在記憶體裡面中建立儲存空間。所以當程式碼逐行執行時,可以找到它們。

所以函式跟它的內容(定義)會先存至記憶體,所以可以正確的呼叫,呼叫後建立這個函式的執行環境,印出 Called b!

雖然變數也在記憶體中建立空間,可是它的內容並沒有在創造階段時被放入。這邊可以把創造變數與賦值給變數當成兩件事,所以呼叫變數 a 才會印出 undefined

上面的程式碼,在電腦執行時可以想像成這樣的順序,結果會是一樣的:

1
2
3
4
5
6
7
var a;
function b() {
console.log("called b !");
}
b();
console.log(a);
a = "hello world";

那如果是下列狀況呢?

1
2
3
4
5
6
7
b();

function b() {
console.log("called b !");
var a = "hello world";
console.log(a);
}

結果可以正常顯示

這是因為函數會先在記憶體裡創造空間,並建立它的區域變數,再執行呼叫。

可是下列的狀況雖然貌似

1
2
3
4
5
6
7
b();

var b = function() {
console.log("called b !");
var a = "hello world";
console.log(a);
};

結果卻是

執行怎麼理解呢?可以這樣想:

變數 b 先被提升 (創造變數 b 並設定進記憶體),接著呼叫執行 b 函式,為變數 b 賦值(指向)函式。呼叫執行 b 函式時報錯,是因為變數 b 這個時候根本沒有被賦值,何況是指向函式呼叫呢?所以自然報錯 b is not a function。

可以想像成是這樣:

1
2
3
4
5
6
7
8
var b;

b();

b = function() {
var a = "Hello World!";
console.log(a);
};

當然這不代表我們的 JS 程式被改寫成這樣,而只是這種貌似變數與函式被拉到執行環境最前面的現象稱為 Hoisting

重點有三:

  1. 變數宣告跟函式宣告都會提升
  2. 只有宣告會提升,賦值不會提升
  3. 別忘了函式裡面還有傳進來的參數

另一個小例子:

1
2
3
4
5
6
var foo = 1;
var foobar = function() {
console.log(foo);
var foo = 2;
};
foobar();

由於提昇的作用,儘管區域變數 foo 的宣告會先於呼叫 console.log 方法,也就是 區域變數的值將會作為參數傳入成為 console.log 得出的值,而不是在函數外部聲明的全域變數。

但是,由於變數宣告不會提升該值,因此輸出將是 undefined,而不是 2。

在創造階段,函數跟其函數內容(定義)會先存至記憶體,雖然變數也在記憶體中建立空間,可是它的內容並沒有在創造階段時被放入。所以呼叫變數 foo 才會印出 undefined。

延伸閱讀:
https://blog.techbridge.cc/2018/11/10/javascript-hoisting/
https://github.com/aszx87410/blog/issues/34#issuecomment-500890667

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2020 CYC'S BLOG All Rights Reserved.

UV : | PV :