函数式编程是一种编程范式。
// 函数式编程特点:
// 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!
掌握了函数式编程基础后,让我们继续学习:
东巴文(db-w.cn) - 让编程学习更简单
🎯 东巴文寄语:函数式编程是一种强大的编程范式,纯函数、不可变性和高阶函数是其核心概念。掌握函数式编程可以让代码更简洁、更可测试。在 db-w.cn,我们帮你建立函数式编程思维!