JavaScript高阶函数

1. 背景介绍

高阶函数是函数式编程的核心概念之一,在JavaScript中得到了广泛应用。高阶函数是指可以接收函数作为参数和/或返回函数作为结果的函数。它们为代码重用、抽象和组合提供了强大的工具。

JavaScript作为一种多范式语言,天生支持函数式编程风格。随着ES6及后续版本的发布,JavaScript的函数式编程能力得到了进一步增强,使得高阶函数的使用更加便捷和普遍。

2. 高阶函数的功能

高阶函数主要有以下几个功能:

  1. 抽象和封装通用的操作逻辑
  2. 实现函数组合
  3. 创建特定的函数工厂
  4. 实现延迟计算和惰性求值
  5. 增强函数的功能(如缓存、节流等)

3. 使用场景

高阶函数在很多场景下都有应用,主要包括:

  1. 数组操作(如map, filter, reduce等)
  2. 事件处理(如防抖、节流)
  3. 异步编程(如Promise链式调用)
  4. 函数式编程(如柯里化、组合)
  5. 中间件模式(如Express中间件)
  6. 装饰器模式
  7. 依赖注入

4. 详细示例

4.1 基础数组操作

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

// 使用map进行数组转换
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// 使用filter进行数组过滤
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4]

// 使用reduce进行累加
const sum = numbers.reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 15

4.2 高级数组操作

const products = [
  { name: 'Laptop', price: 1000, category: 'Electronics' },
  { name: 'Book', price: 20, category: 'Books' },
  { name: 'Phone', price: 500, category: 'Electronics' },
  { name: 'Desk', price: 300, category: 'Furniture' },
];

// 找出所有电子产品的平均价格
const avgElectronicsPrice = products
  .filter(product => product.category === 'Electronics')
  .map(product => product.price)
  .reduce((sum, price, _, array) => sum + price / array.length, 0);

console.log(avgElectronicsPrice); // 750

4.3 函数组合

const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);

const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;

const compute = compose(square, double, addOne);
console.log(compute(3)); // 64

4.4 柯里化

const curry = (fn) => {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...args2) {
        return curried.apply(this, args.concat(args2));
      }
    }
  };
}

const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);

console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1, 2, 3)); // 6

4.5 函数记忆化 (Memoization)

function memoize(fn) {
  const cache = new Map();
  return function (...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}

// 使用记忆化优化斐波那契数列计算
function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

const memoizedFibonacci = memoize(fibonacci);

console.time('Without memoization');
console.log(fibonacci(40));
console.timeEnd('Without memoization');

console.time('With memoization');
console.log(memoizedFibonacci(40));
console.timeEnd('With memoization');

4.6 函数组合与管道

// 函数组合 (从右到左)
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);

// 管道 (从左到右)
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);

// 示例函数
const addTax = (rate) => (price) => price * (1 + rate);
const formatPrice = (price) => `$${price.toFixed(2)}`;
const discount = (rate) => (price) => price * (1 - rate);

// 使用组合
const calculateTotalCompose = compose(
  formatPrice,
  addTax(0.1),
  discount(0.2)
);

// 使用管道
const calculateTotalPipe = pipe(
  discount(0.2),
  addTax(0.1),
  formatPrice
);

console.log(calculateTotalCompose(100)); // "$88.00"
console.log(calculateTotalPipe(100));    // "$88.00"

4.7 部分应用 (Partial Application)

function partial(fn, ...args) {
  return function(...moreArgs) {
    return fn(...args, ...moreArgs);
  }
}

function greet(greeting, name) {
  return `${greeting}, ${name}!`;
}

const sayHelloTo = partial(greet, "Hello");
console.log(sayHelloTo("John")); // "Hello, John!"

const sayGoodMorningTo = partial(greet, "Good morning");
console.log(sayGoodMorningTo("Alice")); // "Good morning, Alice!"

4.8 函数节流 (Throttle)

function throttle(fn, limit) {
  let inThrottle;
  return function(...args) {
    const context = this;
    if (!inThrottle) {
      fn.apply(context, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  }
}

// 使用示例
const expensiveOperation = () => console.log('Expensive operation');
const throttledOperation = throttle(expensiveOperation, 1000);

// 模拟频繁调用
setInterval(throttledOperation, 100);

// 防抖
function debounce(func, delay) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => func.apply(this, args), delay);
  };
}

// 示例:用户停止输入 500ms 后触发搜索
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', debounce(() => {
  console.log('Search triggered');
}, 500));

4.9 异步操作的组合

// 串行执行Promise
function serialPromises(promises) {
  return promises.reduce((chain, promise) => 
    chain.then(results => 
      promise().then(result => [...results, result])
    ), 
    Promise.resolve([])
  );
}

// 模拟异步操作
const asyncOperation = (id) => () => 
  new Promise(resolve => setTimeout(() => resolve(`Operation ${id} complete`), 1000));

const operations = [
  asyncOperation(1),
  asyncOperation(2),
  asyncOperation(3)
];

serialPromises(operations).then(results => console.log(results));
// 大约3秒后输出:
// ["Operation 1 complete", "Operation 2 complete", "Operation 3 complete"]

防抖和节流

防抖是在事件结束后多久之后执行一次,有可能永远都不会触发,

防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。

  • 搜索框输入,只在用户停止输入一段时间后才发送请求
  • 窗口调整,只在用户停止调整一段时间后才重新计算布局

function debounce (func,wait) {
let timer = null
return function (...args) {
clearTimeout(timer)
timer = setTimeout(()=> {
func(this,args)
},wait)
}
}

节流是在事件一直执行中多久去执行一次,节流是指在一定时间内,只允许函数执行一次。

  • 当触发事件时,设置一个标志
  • 在一定时间内,如果再次触发事件,则忽略
  • 到达设定的时间后,执行函数并重置标志
  • 适用场景:
  • 滚动事件监听
  • 频繁点击按钮
  • 游戏中的移动事件
function throttle (func, limit) { let time = 0 return function (...args) { const now = Date.now() if(now - time > limit){ time = now func.apply(this,args) } } } // function throttle (func, limit) { let timenow = false return function (...args) { if(!timenow){ func.apply(this,args) timenow = now setTimeOut(() => timenow = false,limit) } } }

苏ICP备2025153828号