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) 时,传入的 5 就是它的 number 参数。内部函数仍能读取创建它的那次调用中的 multiplier;这种保留访问能力称为闭包。高阶函数是指接收另一个函数、返回一个函数,或两者兼有的函数。

常见问题

如何传递多个函数? 将每个函数引用放在各自的参数位置,用逗号分隔,并确保顺序与接收函数的形参顺序一致。

回调函数何时运行? 由接收函数决定何时调用回调函数。传入函数本身不会调用它;接收函数可以按自身设计立即调用、稍后调用,或多次调用。

函数可以返回另一个函数吗? 可以。调用外层函数以取得返回的函数,然后按需调用或传递该函数。常见用法之一是创建一个通过闭包捕获配置的函数。