HOWTO · JavaScript

將 JavaScript 函式作為引數傳遞

傳遞 JavaScript 函式的方式與傳遞值相似:傳入 `greet` 會提供函式參照,而不會呼叫函式;寫成 `greet()` 則會立即呼叫它,並傳遞其傳回值。

本頁內容

在 JavaScript 中傳遞函式時,請提供接收函式該函式的參照,例如 greet,而不要呼叫它。寫成 greet() 會立即呼叫函式,傳遞的則是它的回傳值。接收函式會決定是否呼叫收到的函式,以及何時呼叫;這個被提供的函式通常稱為回呼函式。這項區別讓你能提供行為,供另一個函式使用。

將具名函式作為引數傳遞

當你想將操作獨立描述,或重複使用該操作時,具名函式就很實用。在這個範例中,formatGreeting 會接收函式 greet 和一個名字,並以該名字呼叫收到的函式:

function greet(name) {
  return "Hello, " + name + "!";
}

function formatGreeting(callback, name) {
  return callback(name);
}

const message = formatGreeting(greet, "Mina");
console.log(message);

呼叫 formatGreeting(greet, "Mina") 會傳遞兩個值:函式參照 greet 和字串 "Mina"。在接收函式中,callback(name) 會呼叫 greet 並回傳其結果。如果寫成 formatGreeting(greet("Mina"), "Mina"),JavaScript 會在 formatGreeting 開始執行前先呼叫 greet,因此第一個引數會是字串,而不是函式。

將函式與其他引數一併傳遞

函式可以同時接收多個回呼函式和一般值。接收函式可以自行選擇要呼叫哪個回呼函式,以及要提供什麼值。在這裡,applyOperation 會接收一個數字和兩個函式,並將這兩個函式分別套用到該數字:

function applyOperation(value, first, second) {
  return [first(value), second(value)];
}

function double(number) {
  return number * 2;
}

function addThree(number) {
  return number + 3;
}

const results = applyOperation(4, double, addThree);
console.log(results.join(", "));

引數順序很重要,因為 applyOperation 中的參數名稱會依照呼叫時的順序接收值。value 會收到 4,first 會收到 double,而 second 會收到 addThree。此函式會以陣列回傳兩個計算結果。當 API 預期收到回呼函式以及選項或資料時,也可以使用相同方式:先確認參數順序,再以函式名稱傳遞各函式,不要先呼叫它們。

使用匿名函式與箭頭函式作為回呼函式

如果某項操作只在呼叫的位置需要,你可以直接在那裡定義函式,而不必另外宣告具名函式。以下範例先將匿名函式傳遞給 map,再於第二次呼叫 map 時傳遞箭頭函式:

const numbers = [1, 2, 3];

const doubled = numbers.map(function (number) {
  return number * 2;
});
const tripled = numbers.map((number) => number * 3);

console.log(doubled.join(", "));
console.log(tripled.join(", "));
2, 4, 6
3, 6, 9

map 會為陣列中的每個元素呼叫回呼函式,並根據回傳值建立新陣列;回呼函式不需要具名。匿名函式使用 function 關鍵字,箭頭函式則使用 =>,並以運算式主體隱含回傳其值。選擇能清楚表達操作的形式即可。箭頭函式也會使用外層作用域中的詞法 this;如果回呼函式需要自己的 this 值,請使用一般函式,並依照呼叫端 API 文件所述的 this 行為處理。

從另一個函式傳回函式

函式也可以傳回另一個函式。這和將回呼函式作為引數傳遞不同:外層呼叫會建立並傳回一個函式,你可以儲存它並在之後呼叫。例如,makeMultiplier 會傳回一個函式,而該函式會在外層作用域中保留指定的乘數:

function makeMultiplier(multiplier) {
  return function (number) {
    return number * multiplier;
  };
}

const triple = makeMultiplier(3);
console.log(triple(5));

makeMultiplier(3) 會傳回一個函式,因此 triple 參照的就是該函式。接著呼叫 triple(5),便會提供它的 number 引數。內部函式仍可讀取建立它的那次呼叫中的 multiplier;這種保留存取能力稱為閉包。高階函式是指會接收另一個函式、傳回另一個函式,或同時具備這兩種行為的函式。

常見問題

如何傳遞多個函式? 將每個函式參照放在各自的引數位置,以逗號分隔,並依照接收函式的參數順序傳遞。

回呼函式何時執行? 接收函式會決定何時呼叫回呼函式。單純傳遞函式並不會呼叫它;接收端可能依其設計立即呼叫、稍後呼叫,或呼叫多次。

函式可以傳回另一個函式嗎? 可以。呼叫外層函式以取得傳回的函式,再視需要呼叫或傳遞該函式。這種方式常用來建立會透過閉包保留設定值的函式。