2020年6月9日 星期二

《面試官別再問》JavaScript 動態變數怎麼寫,動態呼叫function函式

JavaScript 動態呼叫函式:讓程式碼活起來的實用技!

各位朋友,大家好!上次我們聊了聊 JavaScript 裡怎麼「動態建立變數」,也就是把變數名稱當成物件屬性來用。今天,我們要更進一步,來談談怎麼「動態呼叫函式」。

什麼是動態呼叫函式呢?簡單來說,就是我們不直接寫死要呼叫哪個函式,而是透過一個變數或字串,來決定現在要執行哪一個函式。這在開發一些比較彈性、或者需要根據不同條件執行不同動作的系統時,非常管用。


最推薦:把函式也當成物件的「屬性」來存!

就像上次談到動態變數一樣,最安全、最推薦的方法,就是把您的函式們,也通通整理到一個物件裡面。這樣一來,要動態呼叫哪個函式,就跟動態取得物件屬性一樣簡單。

假設我們正在開發一個訂單處理系統,根據不同的訂單狀態,需要執行不同的處理動作:

JavaScript
// 建立一個物件,專門用來存放各種處理函式
const orderProcessors = {
  // 函式一:處理「待付款」的訂單
  handlePending: function(orderId) {
    console.log(`處理訂單 ${orderId}: 提醒客戶盡快付款...`);
    // 這裡可以寫寄送付款通知、檢查付款狀態等邏輯
  },

  // 函式二:處理「已付款」的訂單
  handlePaid: function(orderId) {
    console.log(`處理訂單 ${orderId}: 準備出貨!`);
    // 這裡可以寫更新庫存、生成出貨單等邏輯
  },

  // 函式三:處理「已取消」的訂單
  handleCancelled: function(orderId) {
    console.log(`處理訂單 ${orderId}: 訂單已取消,通知相關部門。`);
    // 這裡可以寫恢復庫存、發送取消通知等邏輯
  }
};

// 假設這是一個從資料庫或使用者操作拿到的訂單狀態
const currentOrderStatus = "Paid"; // 可能是 "Pending", "Paid", "Cancelled"
const currentOrderId = "ORD-20250719-001";

// 現在,我們要根據 currentOrderStatus 動態呼叫對應的函式
// 怎麼辦到呢?就是利用中括號來取物件屬性!
const handlerFunctionName = `handle${currentOrderStatus}`; // 組合出函式名稱字串 "handlePaid"

// 檢查一下這個函式是否存在,比較保險
if (typeof orderProcessors[handlerFunctionName] === 'function') {
  // 動態呼叫函式!
  orderProcessors[handlerFunctionName](currentOrderId);
  // 如果 currentOrderStatus 是 "Paid",就會執行 handlePaid(currentOrderId)
} else {
  console.log(`找不到處理 ${currentOrderStatus} 狀態的函式。`);
}

// 輸出: 處理訂單 ORD-20250719-001: 準備出貨!

這種做法的優點跟上次說的動態變數很像:

  • 集中管理:所有的相關函式都收納在一個物件裡,程式碼整齊好閱讀。

  • 彈性擴充:以後要新增一種訂單狀態的處理?直接在 orderProcessors 裡加一個新函式就好,不用改動到太多地方。

  • 安全可靠:不會有資安風險,也不會影響效能。

  • 容易除錯:函式都在那邊,哪裡出錯一眼就能找到。


次選項:window 物件 (只在瀏覽器裡面用得到)

跟動態變數一樣,在瀏覽器環境下,如果您把函式宣告成全域的(或者用 function declaration 這種方式),那它也會掛到 window 物件底下。這時候,您也可以透過 window 物件來動態呼叫:

JavaScript
// 注意:這段程式碼只在瀏覽器環境下有效喔!
if (typeof window !== 'undefined') {
  // 宣告一個全域函式
  function sayHello(name) {
    console.log(`哈囉,${name}!很高興見到你!`);
  }

  const funcName = "sayHello";
  const userName = "小明";

  // 檢查一下這個函式是否存在
  if (typeof window[funcName] === 'function') {
    // 動態呼叫全域函式
    window[funcName](userName);
  } else {
    console.log(`找不到函式 ${funcName}。`);
  }
  // 輸出: 哈囉,小明!很高興見到你!
}

這個方法的限制一樣:只適用於瀏覽器環境,而且會增加全域變數和函式的數量,讓您的全域環境變得比較混亂。除非有很特殊的理由,否則還是建議用物件來管理。


千萬要小心:eval() 函數 (絕對不推薦!)

又見到我們的老朋友 eval() 了!理論上,它也可以用來動態呼叫函式:

JavaScript
function greetSomeone(name) {
  console.log(`很高興見到你,${name}!`);
}

const funcToCall = "greetSomeone";
const personName = "陳先生";

// 千萬小心!這行程式碼非常危險!
eval(`${funcToCall}('${personName}')`); // 動態建立並執行字串 "greetSomeone('陳先生')"

// 輸出: 很高興見到你,陳先生!

但就像我們上次說的,eval() 根本就是一個「不定時炸彈」。它的缺點實在太多,包括:

  • 嚴重的資安風險:這是最致命的一點。只要有心人士在您傳給 eval() 的字串裡動手腳,您的網站或系統就可能被控制。

  • 效能問題:執行速度慢,不適合用在需要高效能的地方。

  • 除錯困難:您會很難找到問題出在哪裡。

  • 作用域混亂:可能導致難以預料的行為。

所以,再次強調,為了您程式碼的安全、穩定與可維護性,請務必避免使用 eval() 來動態呼叫函式


結語:物件導向的智慧,讓程式碼更靈活!

JavaScript 在設計上,就是一個非常靈活的語言。當您需要「動態」地去做某些事情時,不管是變數還是函式,最安全、最有效率、也最符合現代 JavaScript 開發模式的做法,就是善用「物件」

把相關的變數和函式組織在物件裡,您就能像操作檔案夾一樣,輕鬆地根據名稱去存取或執行它們。這樣不僅讓您的程式碼更整潔、更易於管理,也大大提升了程式的彈性與可擴充性。

希望這次的分享,能幫助大家在撰寫 JavaScript 時,能更得心應手,寫出既靈活又穩固的程式碼。如果您有其他更棒的方法,或者有任何疑問,都歡迎留言,大家一起交流!

熱門文章