Skip to content

B42. 高阶函数 浏览器 API

在 JavaScript 中,函数是一等公民。我们可以将函数作为参数传入另一个函数、从函数中返回函数,甚至可以存储在变量中。这种强大的特性催生了“高阶函数”这一概念 —— 即接受函数作为参数或返回函数的函数。

此外,浏览器也提供了一系列与 JavaScript 交互的 API,让我们能够实现定时任务、异步操作等功能。本章将介绍回调函数、数组常用方法、定时器(setTimeout / setInterval) 等内容,并结合实际案例帮助你掌握它们的用法。

2.1. 回调函数(Callback Function)

还记得 Python 中使用 sorted([3,1,2], key=lambda x: -x) 排序吗?这里的 lambda 就是回调函数!JavaScript 把这种“函数当参数传”的玩法发挥到了极致,比如按钮点击时触发的函数就是典型回调。

2.1.1. 基本用法

javascript
// 定义回调函数
function greet(name) {
  alert(`你好,${name}!`);
}

// 高阶函数:接受函数作为参数
function processUserInput(callback) {
  let name = prompt("请输入你的名字:");
  callback(name);
}

processUserInput(greet); // 弹出输入框后显示问候

TIP

注意:传递的是函数本身,不是执行结果。 ✅ 正确写法:processUserInput(greet) ❌ 错误写法:processUserInput(greet())

2.1.2. 数组中的常用方法

之前我们对数组进行修改时,都需要先进行 for 循环,有时还要手动创建新数组,再一个个 push。然而,JavaScript 为我们提供了一些常见方法,让我们能直接运用简单的函数批量处理数组元素。

2.1.2.1. forEach:遍历数组

  • 用途:对数组每个元素执行操作,无返回值
  • 示例
    javascript
    const arr = [1, 2, 3];
    arr.forEach(num => {
      const result = num * num * 2;
      console.log(result)
    }); // 输出 2, 8, 18
  • 局限不能通过 return 跳出循环。对于有 break 需求的循环,必须使用传统的 for 循环。

2.1.2.2. map:创建新数组

  • 用途:对每个元素处理后生成新数组
  • 示例
    javascript
    const nums = [1, 2, 3];
    const squares = nums.map(n => n ** 2); // [1, 4, 9]
  • 易错点原数组未被修改,需赋值给新变量

2.1.2.3. filter:筛选元素

  • 用途:返回满足条件的新数组
  • 示例
    javascript
    const even = [1, 2, 3, 4].filter(n => n % 2 === 0); // [2, 4]
  • 易错点:条件函数需返回布尔值,否则结果不可预期

2.1.2.4. reduce:累积计算

  • 用途:从左到右累加,常用于求和、扁平化等
  • 示例
    javascript
    const sum = [1, 2, 3].reduce((acc, cur) => acc + cur, 0); // 6
  • 易错点:需明确提供初始值(如计算乘积需初始值为 1)

2.1.2.5. find / findIndex:查找元素

  • 用途find 返回符合条件的首个元素,findIndex 返回索引
  • 示例
    javascript
    const users = [{id: 1}, {id: 2}];
    const user = users.find(u => u.id === 2); // {id:2}
  • 易错点:若未找到匹配项,find 返回 undefinedfindIndex 返回 -1

2.1.2.6. some / every:条件判断

  • 用途some 任一满足即返回 trueevery 全部满足才返回 true
  • 示例
    javascript
    const hasEven = [1, 3, 5].some(n => n % 2 === 0); // false
  • 易错点:函数含有短路逻辑,不保证检查每一个元素,所以函数内最好不要修改外部变量

2.1.2.7. sort:排序

  • 用途:对数组元素排序(默认按字符串排序)
  • 示例
    javascript
    const nums = [3, 1, 2];
    nums.sort((a, b) => a - b); // [1, 2, 3]
  • 易错点:默认排序会将元素转为字符串比较(如 [10, 5] 会被排序为 ["10", "5"]),所以正常情况下一定要制定排序规则

2.2. 定时器 API:setTimeout 与 setInterval

想做个自动轮播图?或者让数字每秒自动+1?这时候就需要 JavaScript 的“定时器兄弟”—— setTimeout(单次定时)和 setInterval(重复定时)!

2.2.1. setTimeout 用法

javascript
// 3秒后弹窗
const timerId = setTimeout(() => {
  alert("时间到!");
}, 3000);

// 取消定时器
clearTimeout(timerId);

TIP

可以传递参数给回调函数:

javascript
setTimeout(greet, 1000, "小明"); // 相当于 greet("小明")

2.2.2. setInterval 用法

javascript
// 每2秒输出当前时间
const intervalId = setInterval(() => {
  console.log(new Date().toLocaleTimeString());
}, 2000);

// 停止定时器
clearInterval(intervalId);

WARNING

⚠️ 切记:定时器不会自动停止,即使页面已关闭也可能继续运行!务必手动清除。

2.3. 定时器的回收机制

有没有遇到过页面卡死的情况?可能是忘了回收定时器!就像用完充电宝要拔掉一样,不用的定时器一定要手动清除。

2.3.1. 内存泄漏风险

javascript
// 错误示范:未清除的定时器
let count = 0;
setInterval(() => {
  count++;
  console.log(count);
}, 1000);
// 即使离开页面,定时器仍在后台运行!

2.3.2. 正确回收方法

javascript
// 在 Vue/React 等框架中,通常在组件销毁时清除
let timer = null;

function startCount() {
  timer = setInterval(() => {
    // 执行计数或其他逻辑
  }, 1000);
}

function stopCount() {
  clearInterval(timer);
  timer = null; // 避免重复清除
}

TIP

避免重复调用 setInterval 导致多个定时器叠加:

javascript
if (!timer) {
  timer = setInterval(...);
}

📚 知识回顾

知识点内容
回调函数函数可作为参数传递,增强代码灵活性
数组方法forEach(遍历)、map(映射)、filter(过滤)、reduce(累积)、find(查找)、some/every(判断)、sort(排序)
定时器setTimeout(单次)、setInterval(重复)
清除定时器使用 clearTimeout(id)clearInterval(id)
内存管理不使用的定时器要及时清除,防止内存泄漏

🧪 课后练习

✅ 基础题

  1. (选择)以下哪个可以清除定时器?

    • A. clearTimer()
    • B. clearInterval(timer) ✅(假设 timersetInterval 创建的)
    • C. removeTimer(timer)
  2. (填空)让函数3秒后执行:

    javascript
    setTimeout(yourFunction, ___)

    → 答案:3000

  3. (纠错)找出代码错误:

    javascript
    setInterval(() => {}, 1000);
    clearInterval(); // ❌ 错误!未传入定时器ID

🎯 项目题:制作倒计时器

要求

  1. 输入框设置倒计时秒数
  2. 每秒更新剩余时间
  3. 结束时播放音效(提示:用 new Audio("sound.mp3").play()
  4. (拓展)支持暂停/继续功能

HTML 示例结构

html
<input type="number" id="seconds" placeholder="输入秒数">
<button onclick="start()">开始</button>
<button onclick="pause()">暂停</button>
<p id="countdown">等待开始...</p>

JavaScript 示例逻辑

javascript
let countdownInterval = null;
let remaining = 0;

function start() {
  if (countdownInterval) clearInterval(countdownInterval); // 防止重复启动

  remaining = parseInt(document.getElementById('seconds').value);
  document.getElementById('countdown').innerText = remaining;

  countdownInterval = setInterval(() => {
    remaining--;
    if (remaining <= 0) {
      clearInterval(countdownInterval);
      new Audio("sound.mp3").play();
      document.getElementById('countdown').innerText = "倒计时结束!";
    } else {
      document.getElementById('countdown').innerText = remaining;
    }
  }, 1000);
}

function pause() {
  clearInterval(countdownInterval);
  countdownInterval = null;
}

INFO

💡 提示:在开发过程中,建议使用 console.log() 查看状态变化,便于调试。

Built by Vitepress | Apache 2.0 Licensed