JavaScript 設計模式與開發實踐 - Chapter 1

April 13, 2016

Chapter 3

  • 變數的範圍和生存週期。
  • Closure
  • High Order Function

變數的範圍和生存週期

在 JavaScript 了解變數所可以作用的範圍是相當重要的,例如以下的範例:

var a = 1;

var myFunc = function() {
  var b = 10;
  console.log(a);
  console.log(b);
};

myFunc(); // 1, 10
console.log(b); // ReferenceError: b is not defined

在全域的地方宣告了一個 a = 1myFunc function 內宣告了一個 b = 10 的變數,在 myFuncconsole.log(a) 為什麼會有結果呢?

原因是當 function 內部找不到變數時,會開始從程式碼的區塊的範圍往外尋找有沒有變數的存在, 而外部 console.log(b) 會造成 RefferenceError 是因為在 function 內宣告的變數只會存活在 function 區塊內, 所以有時候如果沒有將變數放在對的位置,有時候容易造成變數在參考上的錯誤。

在 ES6 中可以善用 constlet 來避免一些錯誤,相關文章可以參考我之前所記錄的 Beginning-ES6

Closure

Closure 在 JavaScript 也是相當重要的概念,簡單來說 Closure 的概念是:

  • Closure 是一個 function,在回傳這個 function 時,可以讓原本內部的變數繼續存活下去。
  • StackOverflow - How do JavaScript closure work
function say(name) {
  const sayHello = `Hi! ${name}`;
  const myName = function () {
    console.log(`${sayHello}, I'm pj.`);
  };

  return myName;
}

const sentence = say('David');
sentence(); // Hi! David, I'm pj.

在前面所提到的,在 function 內宣告的變數只能存在函式內,透過 Closure 我們可以讓它繼續存活下去。

還有一個很經典的問題,我自己也曾遇到過,假設有個情況是我們透過 onclick 來 console 該 node 的值:

Example 1

會發現一個問題是,不管怎麼點都是 3,這是因為 onclick 是非同步的事件,但是在 for 迴圈內 早就已經執行完畢了,所以我們可以修正如下:

Example 2

透過 Closure 的方式,我們把 i 給保留了下來,當我們 onclick 時,從內部往外尋找變數 i,會找到在 被封閉在 Closure 內的變數 i,所以就可以正確地顯示。

High Order function

High Order Function - 高階函式,意思是:

  • function 可以當作參數被傳遞
  • function 可以當作回傳值輸出
  1. function 可以當作參數被傳遞

以前在寫 JavaScript 時,有時候會看到類似這樣的 function:

function(param1, param2, callback)

以前總是不理解這個 callback 到底是做什麼用的,後來才理解原來 function 可以當作參數被傳遞。

透過以下一個簡單的 Log function 來 console 錯誤訊息的範例來解釋:

function Message(errorMessage) {
  console.log(errorMessage);
}

function Log(param1, param2, callback) {
  console.log(`exec1: ${param1}`);
  console.log(`exec1: ${param2}`);

  const err = new Error('Something Error');

  if (err) {
      callback(err);
  }
}

Log('doA', 'doB', Message);

Message function 被當作參數傳到 Log function 內使用,當有 error 時,使用 Message function 來 console 錯誤訊息。

  1. function 可以當作回傳值輸出

function 當作回傳值輸出是很常見的,這代表說可以透過回傳的 function 在延續內部程式碼的執行或計算。

function addOneAndTwo(param1) {
  let total = 0;
  console.log(`param1: ${param1}`);
  total += param1;

  return function(param2) {
    total += param2;
    console.log(total);
  }
}

const calc = addOneAndTwo(1);
calc(2); // 3

High Order Function 還有其他常見的一些應用,像是 Currying

Reference