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. 基本用法
// 定义回调函数
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
返回undefined
,findIndex
返回-1
2.1.2.6. some
/ every
:条件判断
- 用途:
some
任一满足即返回true
,every
全部满足才返回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 用法
// 3秒后弹窗
const timerId = setTimeout(() => {
alert("时间到!");
}, 3000);
// 取消定时器
clearTimeout(timerId);
TIP
可以传递参数给回调函数:
setTimeout(greet, 1000, "小明"); // 相当于 greet("小明")
2.2.2. setInterval 用法
// 每2秒输出当前时间
const intervalId = setInterval(() => {
console.log(new Date().toLocaleTimeString());
}, 2000);
// 停止定时器
clearInterval(intervalId);
WARNING
⚠️ 切记:定时器不会自动停止,即使页面已关闭也可能继续运行!务必手动清除。
2.3. 定时器的回收机制
有没有遇到过页面卡死的情况?可能是忘了回收定时器!就像用完充电宝要拔掉一样,不用的定时器一定要手动清除。
2.3.1. 内存泄漏风险
// 错误示范:未清除的定时器
let count = 0;
setInterval(() => {
count++;
console.log(count);
}, 1000);
// 即使离开页面,定时器仍在后台运行!
2.3.2. 正确回收方法
// 在 Vue/React 等框架中,通常在组件销毁时清除
let timer = null;
function startCount() {
timer = setInterval(() => {
// 执行计数或其他逻辑
}, 1000);
}
function stopCount() {
clearInterval(timer);
timer = null; // 避免重复清除
}
TIP
避免重复调用 setInterval
导致多个定时器叠加:
if (!timer) {
timer = setInterval(...);
}
📚 知识回顾
知识点 | 内容 |
---|---|
回调函数 | 函数可作为参数传递,增强代码灵活性 |
数组方法 | forEach (遍历)、map (映射)、filter (过滤)、reduce (累积)、find (查找)、some/every (判断)、sort (排序) |
定时器 | setTimeout (单次)、setInterval (重复) |
清除定时器 | 使用 clearTimeout(id) 和 clearInterval(id) |
内存管理 | 不使用的定时器要及时清除,防止内存泄漏 |
🧪 课后练习
✅ 基础题
(选择)以下哪个可以清除定时器?
- A.
clearTimer()
- B.
clearInterval(timer)
✅(假设timer
是setInterval
创建的) - C.
removeTimer(timer)
- A.
(填空)让函数3秒后执行:
javascriptsetTimeout(yourFunction, ___)
→ 答案:
3000
(纠错)找出代码错误:
javascriptsetInterval(() => {}, 1000); clearInterval(); // ❌ 错误!未传入定时器ID
🎯 项目题:制作倒计时器
要求:
- 输入框设置倒计时秒数
- 每秒更新剩余时间
- 结束时播放音效(提示:用
new Audio("sound.mp3").play()
) - (拓展)支持暂停/继续功能
HTML 示例结构:
<input type="number" id="seconds" placeholder="输入秒数">
<button onclick="start()">开始</button>
<button onclick="pause()">暂停</button>
<p id="countdown">等待开始...</p>
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()
查看状态变化,便于调试。