2014
Mar
21

網頁好讀版


Javascript 是一個很特別的程式語言,與傳統的 C 語言有很大的落差,這篇文章會介紹兩個 Javascript 獨有的特性 Hoisting 與 Closure , Closure 也有人翻譯成閉包

Closure

Closure 很特別,目前在各種程式語言中,很少會支援這個特性的,一般來說變數的生命週期,只會生存在該 function ,一旦離開了 function ,變數便會被回收,不能夠再被使用,而且每個在 function 中的變數,一定要事先宣告,在 function 中的變數,我們稱之為 Local Variable,function 只能使用自身的 Local Variable ,不能使用 function 外的變數,但是在 Javascript 中卻沒有這麼簡單,來看一個 function 使用外部變數的範例。

closure1.js
  1. var a = 1;
  2.  
  3. function closure1() {
  4. console.log(a)
  5. }
  6. closure1();

上一段程式會印出 「1 」,然而在 function closure1 中沒有宣告 a 這個變數,但是 closure 的 parent 中宣告了 a 這個變數,所以 Javascript 會自動去取得 a =1 。

再來看一個使用暱名 function 的範例。

closure2.js
  1. function closure2() {
  2. var a = 2;
  3. var s = function () {
  4. console.log(a);
  5. }
  6. s();
  7.  
  8. }
  9.  
  10. closure2();

這段程式會印出 「2」,原理跟第一個範例相同, closure2 中執行了 s() ,所以 s() 的 parent 是 closure2 ,根據 closure 的特性 s() 可以使用 closure2 的變數。

再來看一個奇怪的範例,你應該知道輸出的結果是什麼了吧。

closure3
  1. var a = 1;
  2. function closure3() {
  3. var s = function () {
  4. console.log(a);
  5. }
  6. s();
  7. }
  8. closure3();
Answer = 1 , s() -> parent (closure3) -> parent (global)

使用 Closure 時要特別小心,不好的 Closure 很容易造成 memory leak ,其實筆者我平常是不使用 Closure 這種寫法的。

Hoisting

Hoisting 跟變數的宣告有關系,一個 JS function 可以重覆定義相同的變數名稱,如果在 C 語言你重覆定義相同的變數的話,你連 Compile 都會過不了,但是這在 Javascript 中是完全合法的。

Hoisting1
  1. var a = 1;
  2. function hoisting1() {
  3. if (!a) {
  4. var a = 2;
  5. }
  6. console.log(a);
  7.  
  8. }
  9.  
  10. hoisting1();

上一段程式,會印出 「2 」這個值,疑! 剛剛才說過 closure 的觀念, a 不是應該就自動去取得他的 parent 的值嗎 (a=1)。

Javascript 有另一個特性 Hoisting ,程式會將 function 中全部需要宣告的 Local Variable ,提升到 function 的第一行來執行。

來看看 Javascript 真正的執行過程:

Hoisting1 in javascript engine
  1. var a = 1;
  2. function hoisting1() {
  3. var a;
  4. if (!a) {
  5. a = 2;
  6.  
  7. }
  8. console.log(a);
  9. }
  10.  
  11. hoisting1();

看完真正 JS Engine 的執行碼,就可以很清楚的知道原因, Local Variable a 在 function 的第一行就已經被宣告了,這時 a 會等於「undefined」,所以才造成程式進入 if (!a) 的區塊。


網頁好讀版