高阶函数

高阶函数概念

高阶函数是指满足以下条件之一的函数:

  1. 接收一个或多个函数作为参数
  2. 返回一个函数

什么是高阶函数

// 接收函数作为参数
function execute(fn, value) {
    return fn(value);
}

// 返回函数
function createMultiplier(factor) {
    return function(number) {
        return number * factor;
    };
}

// 两者兼备
function compose(f, g) {
    return function(x) {
        return f(g(x));
    };
}

为什么使用高阶函数

// 不使用高阶函数
const numbers = [1, 2, 3, 4, 5];
const doubled = [];
for (let i = 0; i < numbers.length; i++) {
    doubled.push(numbers[i] * 2);
}

// 使用高阶函数
const doubled2 = numbers.map(n => n * 2);

// 好处:
// 1. 代码更简洁
// 2. 更易复用
// 3. 更易测试
// 4. 声明式编程

函数作为参数

将函数作为参数传递是高阶函数的核心用法。

回调函数

// 回调函数模式
function processData(data, callback) {
    const result = data.map(callback);
    return result;
}

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

const doubled = processData(numbers, n => n * 2);
const squared = processData(numbers, n => n * n);

console.log(doubled);   // [2, 4, 6, 8, 10]
console.log(squared);   // [1, 4, 9, 16, 25]

数组方法示例

const users = [
    { name: "东巴文", age: 1 },
    { name: "JavaScript", age: 27 },
    { name: "TypeScript", age: 10 }
];

// filter:过滤
const adults = users.filter(user => user.age >= 18);

// map:映射
const names = users.map(user => user.name);

// find:查找
const found = users.find(user => user.name === "东巴文");

// some:存在判断
const hasAdult = users.some(user => user.age >= 18);

// every:全部判断
const allNamed = users.every(user => user.name);

// sort:排序
const sorted = [...users].sort((a, b) => a.age - b.age);

// reduce:归约
const totalAge = users.reduce((sum, user) => sum + user.age, 0);

自定义高阶函数

// 条件过滤
function filterBy(array, predicate) {
    const result = [];
    for (let item of array) {
        if (predicate(item)) {
            result.push(item);
        }
    }
    return result;
}

const numbers = [1, 2, 3, 4, 5, 6];
const evens = filterBy(numbers, n => n % 2 === 0);
console.log(evens);  // [2, 4, 6]

// 条件查找
function findBy(array, predicate) {
    for (let item of array) {
        if (predicate(item)) {
            return item;
        }
    }
    return undefined;
}

// 条件分组
function groupBy(array, keyFn) {
    return array.reduce((groups, item) => {
        const key = keyFn(item);
        if (!groups[key]) {
            groups[key] = [];
        }
        groups[key].push(item);
        return groups;
    }, {});
}

const users = [
    { name: "东巴文", role: "admin" },
    { name: "张三", role: "user" },
    { name: "李四", role: "user" }
];

const grouped = groupBy(users, u => u.role);
// { admin: [...], user: [...] }

函数作为返回值

返回函数可以创建可配置的函数。

函数工厂

// 创建验证器
function createValidator(rules) {
    return function(value) {
        for (let rule of rules) {
            const error = rule(value);
            if (error) return error;
        }
        return null;
    };
}

const validatePassword = createValidator([
    v => v.length < 8 ? "密码至少8位" : null,
    v => !/[A-Z]/.test(v) ? "需要大写字母" : null,
    v => !/[0-9]/.test(v) ? "需要数字" : null
]);

console.log(validatePassword("abc"));      // "密码至少8位"
console.log(validatePassword("abcdefgh")); // "需要大写字母"
console.log(validatePassword("Abcdefgh")); // "需要数字"
console.log(validatePassword("Abcdefg1")); // null

配置化函数

// 创建API请求函数
function createApiClient(baseUrl) {
    return function(endpoint, options = {}) {
        return fetch(`${baseUrl}${endpoint}`, {
            ...options,
            headers: {
                "Content-Type": "application/json",
                ...options.headers
            }
        }).then(res => res.json());
    };
}

const api = createApiClient("https://api.example.com");

api("/users");           // GET https://api.example.com/users
api("/users", { method: "POST", body: JSON.stringify({ name: "东巴文" }) });

延迟执行

// 延迟调用
function delay(fn, ms) {
    return function(...args) {
        return new Promise(resolve => {
            setTimeout(() => {
                resolve(fn.apply(this, args));
            }, ms);
        });
    };
}

const delayedLog = delay(console.log, 1000);
delayedLog("东巴文");  // 1秒后输出

// 条件执行
function when(condition, fn) {
    return function(...args) {
        if (condition(...args)) {
            return fn.apply(this, args);
        }
    };
}

柯里化

柯里化是将多参数函数转换为一系列单参数函数的技术。

基本概念

// 普通函数
function add(a, b, c) {
    return a + b + c;
}

// 柯里化后
function curriedAdd(a) {
    return function(b) {
        return function(c) {
            return a + b + c;
        };
    };
}

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

通用柯里化函数

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

// 使用
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
console.log(curriedAdd(1, 2, 3));    // 6

柯里化的应用

// 日志函数
const log = curry((level, message) => {
    console.log(`[${level}] ${message}`);
});

const info = log("INFO");
const error = log("ERROR");

info("用户登录");   // [INFO] 用户登录
error("系统错误");  // [ERROR] 系统错误

// 过滤函数
const filter = curry((predicate, array) => {
    return array.filter(predicate);
});

const filterEvens = filter(n => n % 2 === 0);
const filterPositives = filter(n => n > 0);

console.log(filterEvens([1, 2, 3, 4, 5, 6]));  // [2, 4, 6]

// 映射函数
const map = curry((fn, array) => array.map(fn));
const double = map(n => n * 2);
console.log(double([1, 2, 3]));  // [2, 4, 6]

偏函数

偏函数是固定函数的部分参数,返回一个新函数。

基本概念

// 使用bind创建偏函数
function greet(greeting, name) {
    console.log(`${greeting}, ${name}!`);
}

const sayHello = greet.bind(null, "你好");
sayHello("东巴文");  // 你好, 东巴文!

通用偏函数

function partial(fn, ...presetArgs) {
    return function(...laterArgs) {
        return fn.apply(this, [...presetArgs, ...laterArgs]);
    };
}

function multiply(a, b, c) {
    return a * b * c;
}

const double = partial(multiply, 2);
console.log(double(3, 4));  // 24

const doubleAndTriple = partial(multiply, 2, 3);
console.log(doubleAndTriple(4));  // 24

占位符偏函数

const _ = Symbol("placeholder");

function partialRight(fn, ...presetArgs) {
    return function(...laterArgs) {
        const args = [];
        let presetIndex = 0;
        let laterIndex = 0;
        
        for (let i = 0; i < fn.length; i++) {
            if (presetArgs[i] === _) {
                args.push(laterArgs[laterIndex++]);
            } else {
                args.push(presetArgs[presetIndex++]);
            }
        }
        
        return fn.apply(this, args);
    };
}

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

const sayHello = partialRight(greet, "你好", _);
console.log(sayHello("东巴文"));  // 你好, 东巴文!

函数组合

函数组合是将多个函数组合成一个新函数。

基本概念

// 两个函数组合
function compose(f, g) {
    return function(x) {
        return f(g(x));
    };
}

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

const doubleThenAdd = compose(addOne, double);
console.log(doubleThenAdd(3));  // 7 (3 * 2 + 1)

多函数组合

function compose(...fns) {
    return function(x) {
        return fns.reduceRight((acc, fn) => fn(acc), x);
    };
}

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

const calculate = compose(double, addOne, square);
// 执行顺序:square -> addOne -> double
console.log(calculate(3));  // 20 ((3 * 3) + 1) * 2

管道(从左到右)

function pipe(...fns) {
    return function(x) {
        return fns.reduce((acc, fn) => fn(acc), x);
    };
}

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

const calculate = pipe(double, addOne, square);
// 执行顺序:double -> addOne -> square
console.log(calculate(3));  // 49 ((3 * 2) + 1) ^ 2

实际应用

// 数据处理管道
const data = [
    { name: "东巴文", score: 85 },
    { name: "张三", score: 60 },
    { name: "李四", score: 90 },
    { name: "王五", score: 75 }
];

const process = pipe(
    arr => arr.filter(item => item.score >= 70),
    arr => arr.sort((a, b) => b.score - a.score),
    arr => arr.map(item => item.name)
);

console.log(process(data));  // ["李四", "东巴文", "王五"]

记忆化

记忆化是缓存函数结果以避免重复计算的技术。

基本实现

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 fib(n) {
    if (n <= 1) return n;
    return fib(n - 1) + fib(n - 2);
}
// fib(40) 非常慢

// 使用记忆化
const fastFib = memoize(function fib(n) {
    if (n <= 1) return n;
    return fastFib(n - 1) + fastFib(n - 2);
});
// fastFib(40) 瞬间完成

实际应用

// 缓存API请求
const memoizedFetch = memoize(async function(url) {
    const response = await fetch(url);
    return response.json();
});

// 第一次请求会发送网络请求
memoizedFetch("/api/users");

// 第二次直接返回缓存结果
memoizedFetch("/api/users");

带过期时间的记忆化

function memoizeWithExpiry(fn, expiryMs) {
    const cache = new Map();
    
    return function(...args) {
        const key = JSON.stringify(args);
        const cached = cache.get(key);
        
        if (cached && Date.now() - cached.time < expiryMs) {
            return cached.value;
        }
        
        const result = fn.apply(this, args);
        cache.set(key, { value: result, time: Date.now() });
        return result;
    };
}

下一步

掌握了高阶函数后,让我们继续学习:

  1. this与执行上下文 - 深入理解this
  2. 数组基础 - 学习数组操作
  3. 数组方法 - 掌握数组方法

东巴文(db-w.cn) - 让编程学习更简单

🎼 东巴文寄语:高阶函数是函数式编程的核心,掌握高阶函数能让你的代码更加简洁、可复用、易测试。柯里化、组合、记忆化都是强大的工具。在 db-w.cn,我们帮你掌握函数式编程的精髓!