来聊聊
js
中一个超级实用的概念------高阶函数
。别被这个名字吓到,其实它比你想象的要简单得多,而且每天都在用,只是你可能没意识到而已。
一、什么是高阶函数?
简单来说,高阶函数就是"操作函数的函数"。它有两种表现形式:
- 把函数当参数传递:就像你可以把数字、字符串传给函数一样,你也可以把函数作为参数传递
- 把函数当返回值:函数执行后不返回常规值,而是返回另一个函数
javascript
// 1. 函数作为参数
function sayHi(greetingFn) {
greetingFn(); // 调用传入的函数
}
sayHi(function() {
console.log("你好呀!");
});
// 2. 函数作为返回值
function createGreeter() {
return function() {
console.log("我来自内部函数!");
};
}
const myGreeter = createGreeter();
myGreeter(); // 输出:我来自内部函数!
二、为什么需要高阶函数?
想象一下,如果没有高阶函数,我们写代码会多麻烦:
- 事件处理没法写了(addEventListener需要传函数)
- 定时器没法用了(setTimeout需要传函数)
- 数组操作变得复杂(map、filter都用不了)
高阶函数让JavaScript变得灵活强大,是函数式编程的基础。
三、日常开发中的高阶函数
1. 数组的超级帮手
数组方法里到处都是高阶函数,我们来看几个最常用的:
map - 数组变形专家
javascript
const prices = [10, 20, 30];
const discounted = prices.map(price => price * 0.9); // 打9折
console.log(discounted); // [9, 18, 27]
filter - 数组过滤器
javascript
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(num => num % 2 === 0); // 只留偶数
console.log(evens); // [2, 4]
reduce - 数组计算器
javascript
const cart = [
{ name: "苹果", price: 10 },
{ name: "香蕉", price: 20 },
{ name: "橙子", price: 15 }
];
const total = cart.reduce((sum, item) => sum + item.price, 0);
console.log(total); // 45
2. 异步编程好伙伴
setTimeout - 定时执行
javascript
setTimeout(() => {
console.log("3秒后执行");
}, 3000);
Promise.then - 异步处理
javascript
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log("获取到数据:", data));
3. 事件处理必备
javascript
document.getElementById('myBtn').addEventListener('click', () => {
console.log("按钮被点击啦!");
});
四、手把手教你写高阶函数
理解了概念后,我们来动手写几个实用的高阶函数:
1. 函数节流(throttle)
防止按钮重复点击或滚动事件频繁触发:
javascript
function throttle(fn, delay) {
let lastTime = 0;
return function() {
const now = Date.now();
if (now - lastTime >= delay) {
fn.apply(this, arguments);
lastTime = now;
}
};
}
// 使用示例
const throttledScroll = throttle(() => {
console.log("处理滚动...");
}, 200);
window.addEventListener('scroll', throttledScroll);
2. 数据校验器
javascript
function createValidator(rules) {
return function(data) {
for (const [key, checkFn] of Object.entries(rules)) {
if (!checkFn(data[key])) {
return false;
}
}
return true;
};
}
// 使用示例
const validateUser = createValidator({
name: name => name.length >= 2,
age: age => age >= 18
});
console.log(validateUser({ name: "张三", age: 20 })); // true
console.log(validateUser({ name: "李", age: 15 })); // false
3. 缓存函数
javascript
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (cache[key] === undefined) {
cache[key] = fn.apply(this, args);
}
return cache[key];
};
}
// 使用示例
const expensiveCalc = memoize(function(n) {
console.log("长时间计算...");
return n * n;
});
console.log(expensiveCalc(5)); // 输出"长时间计算..."然后返回25
console.log(expensiveCalc(5)); // 直接返回25,不再计算
五、高阶函数的链式调用
高阶函数最爽的地方就是可以像乐高积木一样组合使用:
javascript
const products = [
{ name: "手机", price: 5999, stock: 10 },
{ name: "笔记本", price: 8999, stock: 5 },
{ name: "平板", price: 2999, stock: 20 }
];
// 找出价格低于8000的商品,打8折,然后计算总价
const total = products
.filter(product => product.price < 8000) // 先过滤
.map(product => product.price * 0.8) // 再打折
.reduce((sum, price) => sum + price, 0); // 最后求和
console.log(total); // 输出打折后的总价
六、常见问题解答
Q:高阶函数会不会影响性能? A:确实会有轻微性能开销,因为多了函数调用。但在大多数情况下,代码可读性和维护性的提升远大于这点性能损失。只有在极端性能敏感的场景才需要考虑优化。
Q:箭头函数和高阶函数有什么关系? A:箭头函数让高阶函数写起来更简洁。比如arr.map(x => x*2)
比arr.map(function(x){ return x*2 })
简洁多了。
Q:什么时候该自己写高阶函数? A:当你发现有多处相似代码,只有部分逻辑不同时,就可以考虑用高阶函数来抽象重复部分。
七、总结
高阶函数不是什么高深的概念,它就是:
能接收函数的函数
能返回函数的函数
日常开发中,我们其实经常在用高阶函数
,比如:
数组的map、filter、reduce
定时器setTimeout
事件监听addEventListener
Promise的then/catch
掌握高阶函数后,你会发现:
代码更简洁了
重复代码变少了
逻辑更容易理解了
抽象能力更强了
记住,学习高阶函数最好的方式就是多用、多练。刚开始可能会觉得有点绕,但用多了就会觉得特别自然。