Javascript 零基础教程

JavaScript 函数作为参数

将函数作为参数传递是 JavaScript 的一个核心概念,它能实现强大且灵活的代码编写。

这是高阶函数 (Higher-Order Functions) 的基础。高阶函数是指那些可以接受函数作为参数,或者将函数作为返回值的函数。这种能力允许你编写更抽象、更可重用的代码,从而构建更高效、更易维护的应用程序。

通过理解如何将函数作为参数传递,你将解锁现代 JavaScript 开发中必不可少的高级编程技巧。

1. 理解函数作为一等公民 (First-Class Citizens)

在 JavaScript 中,函数被视为“一等公民”。这意味着它们可以像任何其他变量一样被对待。你可以将它们赋值给变量,将它们作为参数传递给其他函数,也可以从其他函数中返回它们。这种灵活性正是我们能够创建高阶函数的原因。

1.1 函数作为变量

在深入探讨将函数作为参数传递之前,理解函数可以被赋值给变量至关重要。

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

// 将函数 'greet' 赋值给变量 'sayHello'
let sayHello = greet;

// 现在你可以使用变量 'sayHello' 来调用该函数
console.log(sayHello("爱丽丝")); // 输出: 你好, 爱丽丝!

在这个例子中,greet 是一个函数,我们将它赋值给了变量 sayHello。变量 sayHello 现在持有对 greet 函数的引用,允许我们通过这个新变量来调用它。

1.2 圆括号的重要性

区分 greetgreet() 非常重要:

  • greet 指的是函数本身。
  • greet() 调用或执行该函数并返回其值。

当你将函数作为参数传递时,你传递的是函数本身(即 greet),而不是调用函数的结果(即 greet())。

2. 将函数作为参数传递:基础知识

核心思想是你可以将一个函数作为参数传递给另一个函数。接收另一个函数作为参数的函数被称为高阶函数

被传递的函数通常被称为“回调函数 (Callback Function)”,因为高阶函数会在其执行过程中的某个时刻“回头调用 (call back)”它。

function operate(num1, num2, operation) {
  return operation(num1, num2);
}

function add(a, b) {
  return a + b;
}

function subtract(a, b) {
  return a - b;
}

// 将 'add' 函数作为参数传递给 'operate'
let sum = operate(5, 3, add); // sum 将是 8
console.log(sum);

// 将 'subtract' 函数作为参数传递给 'operate'
let difference = operate(5, 3, subtract); // difference 将是 2
console.log(difference);

在这个例子中:

  • operate 是一个高阶函数。它接受两个数字和另一个函数 (operation) 作为参数。
  • addsubtract 是分别执行加法和减法的普通函数。
  • 我们将 addsubtract 作为参数传递给 operate,允许 operate 动态地执行不同的操作。

2.1 匿名函数作为参数

你也可以直接将匿名函数(没有名字的函数)作为参数传递。这在函数只需要在特定位置使用一次时非常常见。

function applyOperation(arr, operation) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    result.push(operation(arr[i]));
  }
  return result;
}

let numbers = [1, 2, 3, 4, 5];

// 传递一个匿名函数来计算数组中每个数字的平方
let squaredNumbers = applyOperation(numbers, function(x) {
  return x * x;
});

console.log(squaredNumbers); // 输出: [1, 4, 9, 16, 25]

在这里,我们将匿名函数 function(x) { return x * x; } 传递给了 applyOperation 函数。这个匿名函数对数组的每个元素进行平方运算。

2.2 箭头函数作为参数

使用 ES6,箭头函数为编写匿名函数提供了更简洁的语法,使代码更具可读性。

function transformArray(arr, transform) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    result.push(transform(arr[i]));
  }
  return result;
}

let numbers = [1, 2, 3, 4, 5];

// 传递一个箭头函数将数组中的每个数字翻倍
let doubledNumbers = transformArray(numbers, (x) => x * 2);

console.log(doubledNumbers); // 输出: [2, 4, 6, 8, 10]

这个例子与前一个类似,但为了简洁使用了箭头函数 (x) => x * 2。箭头函数对于简短、简单的操作特别有用。如果你需要复习它们的语法和行为,请参考第 6 模块关于箭头函数的内容。

2.3 将函数作为参数传递的好处

  • 抽象 (Abstraction): 高阶函数抽象掉了具体执行的操作。上面的 operateapplyOperationtransformArray 函数并不关心什么操作正在被执行;它们只关心如何应用它。
  • 复用性 (Reusability): 你可以使用不同的回调函数重复使用同一个高阶函数来获得不同的结果。
  • 灵活性 (Flexibility): 它允许你通过传递不同的函数作为参数,在运行时动态改变函数的行为。
  • 代码可读性 (Code Readability): 使用匿名函数或箭头函数作为参数,特别是对于简单的操作,可以使你的代码更简洁、更易于理解。

3. 高级示例和用例

3.1 使用自定义比较函数排序

数组的 sort 方法可以接受一个比较函数作为参数,以自定义排序顺序。

let people = [
  { name: "查理", age: 30 },
  { name: "鲍勃", age: 25 },
  { name: "爱丽丝", age: 35 }
];

// 按年龄对人员数组进行排序
people.sort(function(a, b) {
  return a.age - b.age;
});

console.log(people);
// 输出:
// [
//   { name: "鲍勃", age: 25 },
//   { name: "查理", age: 30 },
//   { name: "爱丽丝", age: 35 }
// ]

在这个例子中,sort 方法使用提供的比较函数来确定元素的顺序。

3.2 DOM 操作中的事件监听器

在 Web 开发中,事件监听器是在特定事件(例如按钮点击)发生时执行的函数。这些事件监听器作为参数传递给 addEventListener 等方法。

<!DOCTYPE html>
<html>
<head>
  <title>事件监听器示例</title>
</head>
<body>
  <button id="myButton">点击我</button>
  <script>
    let button = document.getElementById("myButton");
    
    // 向按钮添加事件监听器
    button.addEventListener("click", function() {
      alert("按钮被点击了!");
    });
  </script>
</body>
</html>

在这里,匿名函数 function() { alert("按钮被点击了!"); } 被作为参数传递给 addEventListener。当按钮被点击时,该函数将被执行。

3.3 定时器和异步操作

setTimeoutsetInterval 这样的函数接受一个回调函数作为参数,该函数会在指定的延迟后或以固定的间隔执行。

// 2秒后执行一个函数
setTimeout(function() {
  console.log("这条消息将在 2 秒后显示。");
}, 2000);

// 每 1 秒执行一个函数
let intervalId = setInterval(function() {
  console.log("这条消息将每隔 1 秒显示一次。");
}, 1000);

// 5秒后停止间隔
setTimeout(function() {
  clearInterval(intervalId);
  console.log("间隔已停止。");
}, 5000);