函数式编程基础

函数式编程概念

函数式编程是一种编程范式。

什么是函数式编程

// 函数式编程特点:
// 1. 函数是一等公民
// 2. 纯函数:相同输入产生相同输出
// 3. 不可变性:数据不可修改
// 4. 声明式编程:描述做什么,而非怎么做
// 5. 避免副作用:不修改外部状态

// 命令式编程
const arr = [1, 2, 3, 4, 5];
const doubled = [];
for (let i = 0; i < arr.length; i++) {
    doubled.push(arr[i] * 2);
}

// 函数式编程
const doubled2 = [1, 2, 3, 4, 5].map(x => x * 2);

函数是一等公民

// 函数可以赋值给变量
const greet = function(name) {
    return `Hello, ${name}`;
};

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

console.log(execute(greet, "东巴文"));

// 函数可以作为返回值
function createGreeter(greeting) {
    return function(name) {
        return `${greeting}, ${name}`;
    };
}

const sayHello = createGreeter("Hello");
console.log(sayHello("东巴文"));

// 函数可以存储在数据结构中
const operations = {
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
    multiply: (a, b) => a * b
};

纯函数

没有副作用的函数。

纯函数特点

// 纯函数:
// 1. 相同输入总是返回相同输出
// 2. 不依赖外部状态
// 3. 不修改外部状态

// 纯函数示例
function add(a, b) {
    return a + b;
}

console.log(add(1, 2));  // 总是返回3

// 不纯函数示例
let total = 0;
function addToTotal(value) {
    total += value;  // 修改外部状态
    return total;
}

console.log(addToTotal(1));  // 1
console.log(addToTotal(1));  // 2(结果不同)

副作用

// 常见副作用:
// 1. 修改外部变量
// 2. DOM操作
// 3. 网络请求
// 4. 文件操作
// 5. 控制台输出

// 有副作用
let counter = 0;
function increment() {
    counter++;  // 副作用
    return counter;
}

// 无副作用(纯函数)
function incrementPure(count) {
    return count + 1;
}

// 引用透明性
// 纯函数的调用可以用返回值替换
function double(x) {
    return x * 2;
}

const result = double(2) + double(2);
// 可以替换为
const result2 = 4 + 4;

不可变性

数据不可修改。

不可变数据

// 可变操作
const arr = [1, 2, 3];
arr.push(4);  // 修改原数组

// 不可变操作
const arr2 = [1, 2, 3];
const newArr = [...arr2, 4];  // 创建新数组

// 对象
const obj = { name: "东巴文" };
obj.name = "db-w.cn";  // 可变

const newObj = { ...obj, name: "db-w.cn" };  // 不可变

// 深拷贝
const deepClone = obj => JSON.parse(JSON.stringify(obj));

// 使用Object.freeze
const frozen = Object.freeze({ name: "东巴文" });
// frozen.name = "new";  // 无效(严格模式报错)

不可变操作方法

// 数组不可变操作
const arr = [1, 2, 3];

// 添加元素
const added = [...arr, 4];
const prepended = [0, ...arr];

// 删除元素
const removed = arr.filter(x => x !== 2);

// 修改元素
const modified = arr.map(x => x === 2 ? 20 : x);

// 插入元素
const inserted = [...arr.slice(0, 1), 1.5, ...arr.slice(1)];

// 对象不可变操作
const obj = { a: 1, b: 2 };

// 添加属性
const added = { ...obj, c: 3 };

// 删除属性
const { b, ...removed } = obj;

// 修改属性
const modified = { ...obj, a: 10 };

// 嵌套对象
const nested = { user: { name: "东巴文" } };
const modifiedNested = {
    ...nested,
    user: { ...nested.user, name: "db-w.cn" }
};

高阶函数

操作函数的函数。

函数作为参数

// 高阶函数:接收函数作为参数
function execute(fn, ...args) {
    return fn(...args);
}

console.log(execute(Math.max, 1, 2, 3));  // 3

// 数组方法
const arr = [1, 2, 3, 4, 5];

// map
const doubled = arr.map(x => x * 2);

// filter
const evens = arr.filter(x => x % 2 === 0);

// reduce
const sum = arr.reduce((acc, x) => acc + x, 0);

// find
const found = arr.find(x => x > 3);

// some
const hasEven = arr.some(x => x % 2 === 0);

// every
const allPositive = arr.every(x => x > 0);

函数作为返回值

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

const double = multiplier(2);
const triple = multiplier(3);

console.log(double(5));  // 10
console.log(triple(5));  // 15

// 创建比较函数
function createComparator(key) {
    return function(a, b) {
        return a[key] - b[key];
    };
}

const users = [
    { name: "东巴文", age: 25 },
    { name: "db-w.cn", age: 20 }
];

users.sort(createComparator("age"));

函数组合

组合多个函数。

组合函数

// 函数组合:从右向左执行
function compose(...fns) {
    return function(value) {
        return fns.reduceRight((acc, fn) => fn(acc), value);
    };
}

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

const composed = compose(square, double, addOne);
console.log(composed(3));  // ((3 + 1) * 2)² = 64

// 管道:从左向右执行
function pipe(...fns) {
    return function(value) {
        return fns.reduce((acc, fn) => fn(acc), value);
    };
}

const piped = pipe(addOne, double, square);
console.log(piped(3));  // ((3 + 1) * 2)² = 64

实际应用

// 数据处理管道
const data = [
    { name: "东巴文", age: 25, score: 85 },
    { name: "db-w.cn", age: 20, score: 90 }
];

const process = pipe(
    arr => arr.filter(u => u.score >= 80),
    arr => arr.map(u => ({ ...u, grade: u.score >= 90 ? "A" : "B" })),
    arr => arr.sort((a, b) => b.score - a.score)
);

const result = process(data);

柯里化

将多参数函数转换为单参数函数。

柯里化实现

// 柯里化
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, ...moreArgs]);
        };
    };
}

// 使用
function add(a, b, c) {
    return 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

柯里化应用

// 创建可复用的函数
const formatName = curry((first, last) => `${first} ${last}`);
const formatChineseName = formatName("东巴");
console.log(formatChineseName("文"));  // 东巴 文

// 配置函数
const request = curry((method, url, data) => {
    return fetch(url, { method, body: JSON.stringify(data) });
});

const post = request("POST");
const postUser = post("/api/user");
postUser({ name: "东巴文" });

// 过滤器
const filterBy = curry((key, value, arr) => 
    arr.filter(item => item[key] === value)
);

const users = [
    { name: "东巴文", role: "admin" },
    { name: "db-w.cn", role: "user" }
];

const filterByRole = filterBy("role");
const admins = filterByRole("admin", users);

偏函数应用

固定部分参数。

偏函数实现

// 偏函数应用
function partial(fn, ...presetArgs) {
    return function(...laterArgs) {
        return fn(...presetArgs, ...laterArgs);
    };
}

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

const sayHello = partial(greet, "Hello");
console.log(sayHello("东巴文", "!"));  // Hello, 东巴文!

const sayHelloTo = partial(greet, "Hello", "东巴文");
console.log(sayHelloTo("?"));  // Hello, 东巴文?

// 使用bind实现
const sayHi = greet.bind(null, "Hi");
console.log(sayHi("db-w.cn", "!"));  // Hi, db-w.cn!

下一步

掌握了函数式编程基础后,让我们继续学习:

  1. 函数式编程技术 - 学习高级函数式编程
  2. 错误处理 - 学习错误处理最佳实践
  3. 调试技术 - 学习调试技巧

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

🎯 东巴文寄语:函数式编程是一种强大的编程范式,纯函数、不可变性和高阶函数是其核心概念。掌握函数式编程可以让代码更简洁、更可测试。在 db-w.cn,我们帮你建立函数式编程思维!