JavaScript 作为一门多范式语言,既支持面向对象编程,也为函数式编程(Functional Programming, FP)提供了强大的支持。函数式编程以其声明式、不可变性和高阶函数等特性,正在现代前端开发中占据越来越重要的地位。而柯里化(Currying)作为函数式编程的核心技术之一,通过将多参数函数转换为单参数函数链,极大地提升了代码的灵活性和复用性。
1. 函数式编程的基础
1.1 什么是函数式编程?
函数式编程是一种编程范式,强调将计算过程建模为数学函数的求值,避免状态变化和可变数据。其核心思想包括:
-
纯函数:函数的输出仅依赖于输入,且无副作用。
-
不可变性:数据一旦创建不可修改,变化通过创建新数据实现。
-
高阶函数:函数可以作为参数传递或作为返回值返回。
-
声明式:关注“做什么”而非“怎么做”,代码更简洁。
在 JavaScript 中,函数是一等公民(First-Class Citizen),支持高阶函数、闭包等特性,使其天然适合函数式编程。
1.2 函数式编程的核心原则
-
纯函数:相同的输入始终产生相同的输出,无副作用。例如:
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // 5
反例(非纯函数):
let total = 0;
function addToTotal(value) {
total += value;
return total;
}
-
不可变性:避免直接修改数据:
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2); // [2, 4, 6]
console.log(numbers); // [1, 2, 3](原数组未变)
-
避免副作用:函数不应修改外部状态:
const log = console.log;
function greet(name) {
log(`Hello, ${name}`); // 副作用
return `Hello, ${name}`;
}
-
函数组合:通过组合小函数实现复杂逻辑:
const compose = (f, g) => x => f(g(x));
const addOne = x => x + 1;
const double = x => x * 2;
const addOneThenDouble = compose(double, addOne);
console.log(addOneThenDouble(5)); // 12
1.3 为什么在 JavaScript 中使用函数式编程?
-
可预测性:纯函数和不可变性降低调试难度。
-
模块化:高阶函数和函数组合提升代码复用性。
-
并发友好:无状态操作更适合异步和并发场景。
-
现代框架支持:React、Redux 等框架大量采用函数式思想。
2. 核心函数式编程概念
2.1 纯函数与副作用
纯函数是函数式编程的基石。实现纯函数需遵循:
-
输入决定输出:不依赖外部变量。
-
无外部修改:不更改全局状态或 DOM。
示例:
function filterEvens(numbers) {
return numbers.filter(n => n % 2 === 0);
}
console.log(filterEvens([1, 2, 3, 4])); // [2, 4]
避免副作用:
// 非纯函数
let counter = 0;
function increment() {
counter++;
return counter;
}
// 纯函数
function incrementCounter(current) {
return current + 1;
}
2.2 高阶函数
高阶函数接受函数作为参数或返回函数。JavaScript 的数组方法(如 map
、filter
、reduce
)是典型的高阶函数。
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8]
const evens = numbers.filter(n => n % 2 === 0); // [2, 4]
const sum = numbers.reduce((acc, n) => acc + n, 0); // 10
自定义高阶函数:
function withLogging(fn) {
return (...args) => {
console.log(`Calling ${fn.name} with`, args);
const result = fn(...args);
console.log(`Result:`, result);
return result;
};
}
const add = (a, b) => a + b;
const loggedAdd = withLogging(add);
loggedAdd(2, 3);
// Calling add with [2, 3]
// Result: 5
2.3 闭包与函数式编程
闭包允许函数访问其定义时的词法作用域,是实现函数式编程的重要机制。
function createCounter() {
let count = 0;
return {
increment: () => ++count,
get: () => count,
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.get()); // 1
闭包实现私有状态:
function createUser(name) {
let _name = name;
return {
getName: () => _name,
setName: newName => (_name = newName),
};
}
const user = createUser('Alice');
console.log(user.getName()); // Alice
user.setName('Bob');
console.log(user.getName()); // Bob
2.4 不可变性
JavaScript 中的数组和对象是可变的,需通过复制实现不可变性。
数组不可变操作:
const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4]; // [1, 2, 3, 4]
console.log(numbers); // [1, 2, 3]
对象不可变操作:
const user = { name: 'Alice', age: 30 };
const updatedUser = { ...user, age: 31 };
console.log(user); // { name: 'Alice', age: 30 }
console.log(updatedUser); // { name: 'Alice', age: 31 }
使用 Object.freeze
:
const config = Object.freeze({
apiUrl: 'https://api.example.com',
timeout: 5000,
});
config.apiUrl = 'new-url'; // 无效果
console.log(config.apiUrl); // https://api.example.com
2.5 函数组合与管道
函数组合通过将多个函数串联实现复杂逻辑。
组合(compose
):
const compose = (...fns) => x => fns.reduceRight((y, f) => f(y), x);
const addOne = x => x + 1;
const double = x => x * 2;
const addOneThenDouble = compose(double, addOne);
console.log(addOneThenDouble(5)); // 12
管道(pipe
):
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x);
const addOneThenDouble = pipe(addOne, double);
console.log(addOneThenDouble(5)); // 12
3. 柯里化的核心概念
3.1 什么是柯里化?
柯里化是将一个多参数函数转换为一系列单参数函数的过程。形式上:
// 非柯里化
function add(a, b) {
return a + b;
}
// 柯里化
function curriedAdd(a) {
return function(b) {
return a + b;
};
}
const addFive = curriedAdd(5);
console.log(addFive(3)); // 8
3.2 柯里化的优势
-
参数复用:固定部分参数,生成专用函数。
-
延迟执行:只有提供所有参数时才执行计算。
-
函数组合:柯里化函数易于组合。
-
模块化:将复杂逻辑拆分为小函数。
3.3 手动实现柯里化
简单柯里化:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
}
return (...nextArgs) => curried(...args, ...nextArgs);
};
}
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
3.4 处理任意参数
支持动态参数:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
}
return (...nextArgs) => curried(...args, ...nextArgs);
};
}
const join = (...args) => args.join('');
const curriedJoin = curry(join);
console.log(curriedJoin('a')('b')('c')); // abc
console.log(curriedJoin('a', 'b')('c')); // abc
3.5 占位符支持
实现带占位符的柯里化(如 Lodash 的 _.curry
):
const _ = Symbol('placeholder');
function curry(fn) {
const arity = fn.length;
return function curried(...args) {
const actualArgs = args.filter(arg => arg !== _);
if (actualArgs.length >= arity) {
return fn(...args.slice(0, arity));
}
return (...nextArgs) => {
const combined = [];
let argIdx = 0;
let nextIdx = 0;
for (let i = 0; i < args.length; i++) {
if (args[i] === _ && nextIdx < nextArgs.length) {
combined.push(nextArgs[nextIdx++]);
} else {
combined.push(args[i]);
}
}
while (nextIdx < nextArgs.length) {
combined.push(nextArgs[nextIdx++]);
}
return curried(...combined);
};
};
}
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
console.log(curriedAdd(1, _, 3)(2)); // 6
console.log(curriedAdd(_, 2, _)(1, 3)); // 6
4. 柯里化在函数式编程中的应用
4.1 参数复用
柯里化通过固定参数生成专用函数:
const multiply = curry((a, b) => a * b);
const double = multiply(2);
const triple = multiply(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
4.2 事件处理
为 DOM 事件创建专用处理器:
const addEventListener = curry((event, handler, element) =>
element.addEventListener(event, handler)
);
const onClick = addEventListener('click');
const logClick = () => console.log('Clicked');
const button = document.querySelector('#myButton');
onClick(logClick)(button);
4.3 数据转换
处理数据管道:
const map = curry((fn, arr) => arr.map(fn));
const filter = curry((fn, arr) => arr.filter(fn));
const addOne = x => x + 1;
const isEven = x => x % 2 === 0;
const processNumbers = pipe(
map(addOne),
filter(isEven)
);
console.log(processNumbers([1, 2, 3, 4])); // [2, 4]
4.4 配置化函数
为函数注入配置:
const fetchData = curry((baseUrl, endpoint, params) =>
fetch(`${baseUrl}/${endpoint}?${new URLSearchParams(params)}`).then(res =>
res.json()
)
);
const api = fetchData('https://api.example.com');
const getUsers = api('users');
getUsers({ limit: 10 }).then(console.log);
5. 函数式编程与柯里化在前端框架中的应用
5.1 React 中的函数式编程
React 的函数组件和 Hooks 天然契合函数式编程。
纯组件:
function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
高阶组件(HOC):
const withLoading = Component => ({ isLoading, ...props }) =>
isLoading ? <div>Loading...</div> : <Component {...props} />;
const UserListWithLoading = withLoading(UserList);
function App() {
const [users, setUsers] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(data => {
setUsers(data);
setIsLoading(false);
});
}, []);
return <UserListWithLoading isLoading={isLoading} users={users} />;
}
柯里化在 React:
const useFetch = curry((url, options, setState) =>
useEffect(() => {
fetch(url, options)
.then(res => res.json())
.then(setState);
}, [url, options])
);
function UserList() {
const [users, setUsers] = useState([]);
const fetchUsers = useFetch('/api/users', { method: 'GET' });
fetchUsers(setUsers);
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
5.2 Vue 中的函数式编程
Vue 3 的组合式 API 支持函数式风格。
纯函数组件:
import { defineComponent } from 'vue';
export default defineComponent({
props: ['users'],
setup({ users }) {
return () => (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
},
});
高阶组件:
const withLoading = Component => ({
props: ['isLoading'],
setup(props) {
return () => (props.isLoading ? <div>Loading...</div> : <Component {...props} />);
},
});
const UserListWithLoading = withLoading(UserList);
柯里化:
const useFetch = curry((url, options, setState) => {
onMounted(() => {
fetch(url, options)
.then(res => res.json())
.then(setState);
});
});
export default defineComponent({
setup() {
const users = ref([]);
const fetchUsers = useFetch('/api/users', { method: 'GET' });
fetchUsers(users.value);
return () => (
<ul>
{users.value.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
},
});
6. 函数式编程工具库
6.1 Ramda
Ramda 是一个专注于函数式编程的库,内置柯里化支持。
安装:
npm install ramda
使用:
import * as R from 'ramda';
const addOne = R.map(R.add(1));
const filterEvens = R.filter(x => x % 2 === 0);
const processNumbers = R.pipe(addOne, filterEvens);
console.log(processNumbers([1, 2, 3, 4])); // [2, 4]
柯里化:
const add = R.curry((a, b) => a + b);
const addFive = add(5);
console.log(addFive(3)); // 8
6.2 Lodash/fp
Lodash 的函数式模块提供柯里化支持。
安装:
npm install lodash
使用:
import { flow, map, filter, curry } from 'lodash/fp';
const addOne = map(x => x + 1);
const filterEvens = filter(x => x % 2 === 0);
const processNumbers = flow([addOne, filterEvens]);
console.log(processNumbers([1, 2, 3, 4])); // [2, 4]
const add = curry((a, b) => a + b);
const addFive = add(5);
console.log(addFive(3)); // 8
7. 性能优化
7.1 记忆化
通过缓存结果优化纯函数性能:
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
};
}
const factorial = memoize(n => (n <= 1 ? 1 : n * factorial(n - 1)));
console.log(factorial(5)); // 120
console.log(factorial(5)); // 120(从缓存获取)
7.2 惰性求值
延迟计算以提升性能:
function lazyMap(fn, arr) {
return {
next: () => {
const result = arr.map(fn);
this.next = () => result;
return result;
},
};
}
const numbers = [1, 2, 3, 4];
const lazyDouble = lazyMap(x => x * 2, numbers);
console.log(lazyDouble.next()); // [2, 4, 6, 8]
console.log(lazyDouble.next()); // [2, 4, 6, 8](缓存结果)
7.3 柯里化性能优化
缓存柯里化函数:
function curry(fn) {
const cache = new Map();
return function curried(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
if (args.length >= fn.length) {
const result = fn(...args);
cache.set(key, result);
return result;
}
const next = (...nextArgs) => curried(...args, ...nextArgs);
cache.set(key, next);
return next;
};
}
const add = curry((a, b, c) => a + b + c);
const addFive = add(5);
console.log(addFive(2)(3)); // 10
console.log(addFive(2)(3)); // 10(缓存)
8. 函数式编程与 TypeScript
8.1 纯函数
const add = (a: number, b: number): number => a + b;
console.log(add(2, 3)); // 5
8.2 高阶函数
type Fn<T, R> = (arg: T) => R;
function withLogging<T, R>(fn: Fn<T, R>): Fn<T, R> {
return (...args: T[]) => {
console.log(`Calling ${fn.name} with`, args);
const result = fn(...args);
console.log(`Result:`, result);
return result;
};
}
const add = (a: number, b: number) => a + b;
const loggedAdd = withLogging(add);
loggedAdd(2, 3);
8.3 柯里化
function curry<T extends any[], R>(fn: (...args: T) => R) {
return function curried(...args: any[]): any {
if (args.length >= fn.length) {
return fn(...args);
}
return (...nextArgs: any[]) => curried(...args, ...nextArgs);
};
}
const add = (a: number, b: number, c: number) => a + b + c;
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
8.4 不可变性
interface User {
readonly name: string;
readonly age: number;
}
const user: User = { name: 'Alice', age: 30 };
const updatedUser: User = { ...user, age: 31 };
console.log(user); // { name: 'Alice', age: 30 }
console.log(updatedUser); // { name: 'Alice', age: 31 }
9. 函数式编程与异步编程
9.1 Promise 链
const fetchData = url =>
fetch(url)
.then(res => res.json())
.then(data => data.results);
const processData = pipe(
map(item => ({ ...item, processed: true })),
filter(item => item.id > 0)
);
fetchData('/api/users')
.then(processData)
.then(console.log);
9.2 异步柯里化
const asyncCurry = fn => {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
}
return (...nextArgs) => curried(...args, ...nextArgs);
};
};
const fetchData = async (url, params) =>
fetch(`${url}?${new URLSearchParams(params)}`).then(res => res.json());
const curriedFetch = asyncCurry(fetchData);
const getUsers = curriedFetch('/api/users');
getUsers({ limit: 10 }).then(console.log);
10. 函数式编程与状态管理
10.1 Redux
Redux 使用纯函数(Reducer)管理状态:
const initialState = { count: 0 };
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state;
}
};
const increment = () => ({ type: 'INCREMENT' });
const decrement = () => ({ type: 'DECREMENT' });
柯里化 Action Creator:
const createAction = curry((type, payload) => ({ type, payload }));
const increment = createAction('INCREMENT');
const decrement = createAction('DECREMENT');
console.log(increment({ value: 1 })); // { type: 'INCREMENT', payload: { value: 1 } }
10.2 Zustand
Zustand 支持函数式状态管理:
import create from 'zustand';
const useStore = create(set => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 })),
decrement: () => set(state => ({ count: state.count - 1 })),
}));
const increment = useStore(state => state.increment);
const decrement = useStore(state => state.decrement);
柯里化:
const createAction = curry((fn, set) => (...args) => set(state => fn(state, ...args)));
const increment = createAction((state, value) => ({
count: state.count + value,
}));
const decrement = createAction((state, value) => ({
count: state.count - value,
}));
const useStore = create(set => ({
count: 0,
increment: increment(set),
decrement: decrement(set),
}));
11. 函数式编程与测试
11.1 测试纯函数
describe('add', () => {
it('should add two numbers', () => {
const add = (a, b) => a + b;
expect(add(2, 3)).toBe(5);
});
});
11.2 测试柯里化函数
describe('curriedAdd', () => {
const add = curry((a, b, c) => a + b + c);
it('should add three numbers', () => {
expect(add(1)(2)(3)).toBe(6);
expect(add(1, 2)(3)).toBe(6);
expect(add(1)(2, 3)).toBe(6);
});
});
11.3 Mock 高阶函数
jest.mock('./utils', () => ({
withLogging: jest.fn(fn => (...args) => fn(...args)),
}));
describe('withLogging', () => {
it('should wrap function', () => {
const add = (a, b) => a + b;
const loggedAdd = require('./utils').withLogging(add);
expect(loggedAdd(2, 3)).toBe(5);
expect(require('./utils').withLogging).toHaveBeenCalledWith(add);
});
});
12. 函数式编程与模块化
12.1 CommonJS
// utils.js
module.exports = {
curry: fn => {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
}
return (...nextArgs) => curried(...args, ...nextArgs);
};
},
};
// app.js
const { curry } = require('./utils');
const add = curry((a, b) => a + b);
console.log(add(2)(3)); // 5
12.2 ES Modules
// utils.mjs
export const curry = fn => {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
}
return (...nextArgs) => curried(...args, ...nextArgs);
};
};
// app.mjs
import { curry } from './utils.mjs';
const add = curry((a, b) => a + b);
console.log(add(2)(3)); // 5
13. 函数式编程与微前端
13.1 模块化状态管理
const createModuleStore = curry((moduleName, initialState, reducers) =>
create(set => ({
...initialState,
...Object.keys(reducers).reduce(
(acc, action) => ({
...acc,
[action]: (...args) =>
set(state => reducers[action](state, ...args)),
}),
{}
),
}))
);
const userStore = createModuleStore('user', { users: [] }, {
addUser: (state, user) => ({ users: [...state.users, user] }),
});
userStore.getState().addUser({ name: 'Alice' });
console.log(userStore.getState().users); // [{ name: 'Alice' }]
13.2 Qiankun 微前端
import { registerMicroApps, start } from 'qiankun';
const createMicroApp = curry((name, config) => ({
name,
...config,
}));
const registerApp = curry((apps, app) => {
apps.push(app);
return apps;
});
const apps = [];
const registerReactApp = registerApp(apps);
const createReactApp = createMicroApp('reactApp');
registerReactApp(
createReactApp({
entry: '//localhost:3001',
container: '#reactContainer',
activeRule: '/react',
})
);
registerMicroApps(apps);
start();
14. 函数式编程与错误处理
14.1 Either 容器
实现简化的 Either 容器处理错误:
class Either {
constructor(value, isRight = true) {
this.value = value;
this.isRight = isRight;
}
static Right(value) {
return new Either(value, true);
}
static Left(value) {
return new Either(value, false);
}
map(fn) {
return this.isRight ? Either.Right(fn(this.value)) : this;
}
getOrElse(defaultValue) {
return this.isRight ? this.value : defaultValue;
}
}
const safeDivide = curry((a, b) =>
b === 0 ? Either.Left('Division by zero') : Either.Right(a / b)
);
console.log(safeDivide(10)(2).getOrElse(0)); // 5
console.log(safeDivide(10)(0).getOrElse(0)); // 0
14.2 Try-Catch
const tryCatch = curry((fn, ...args) => {
try {
return Either.Right(fn(...args));
} catch (error) {
return Either.Left(error.message);
}
});
const parseJSON = tryCatch(JSON.parse);
console.log(parseJSON('{"name":"Alice"}').getOrElse({})); // { name: 'Alice' }
console.log(parseJSON('invalid').getOrElse({})); // {}
15. 函数式编程与性能分析
15.1 性能测试
const numbers = Array.from({ length: 1000 }, (_, i) => i);
const start = performance.now();
numbers.map(x => x * 2).filter(x => x % 2 === 0);
const end = performance.now();
console.log(`Map and filter took ${end - start}ms`);
15.2 优化循环
使用 reduce
减少多次遍历:
const processNumbers = numbers =>
numbers.reduce((acc, x) => {
const doubled = x * 2;
if (doubled % 2 === 0) {
acc.push(doubled);
}
return acc;
}, []);
const start = performance.now();
processNumbers(numbers);
const end = performance.now();
console.log(`Reduce took ${end - start}ms`);
16. 函数式编程与 Node.js
16.1 文件操作
const fs = require('fs').promises;
const readFile = curry((encoding, path) => fs.readFile(path, encoding));
const writeFile = curry((path, data) => fs.writeFile(path, data));
const processFile = pipe(
readFile('utf8'),
map(line => line.toUpperCase()),
writeFile('output.txt')
);
processFile('input.txt');
16.2 HTTP 服务器
const http = require('http');
const createRoute = curry((method, path, handler) => ({
method,
path,
handler,
}));
const handleRequest = curry((routes, req, res) => {
const route = routes.find(
r => r.method === req.method && r.path === req.url
);
if (route) {
route.handler(req, res);
} else {
res.writeHead(404);
res.end('Not Found');
}
});
const routes = [
createRoute('GET', '/users', (req, res) => {
res.writeHead(200);
res.end(JSON.stringify([{ name: 'Alice' }]));
}),
];
const server = http.createServer(handleRequest(routes));
server.listen(3000);
17. 函数式编程与事件处理
const createEventHandler = curry((event, handler, element) =>
element.addEventListener(event, handler)
);
const onClick = createEventHandler('click');
const logClick = () => console.log('Clicked');
const button = document.querySelector('#myButton');
onClick(logClick)(button);
18. 函数式编程与数据模型
const createModel = curry((transform, data) => transform(data));
const userModel = createModel(data => ({
id: data.id,
name: data.name,
getFullName: () => data.name,
}));
const user = userModel({ id: 1, name: 'Alice' });
console.log(user.getFullName()); // Alice
19. 函数式编程与插件系统
const createPlugin = curry((name, fn) => ({
name,
apply: fn,
}));
const loggerPlugin = createPlugin('logger', config => message =>
console.log(`[${config.level}] ${message}`)
);
const logger = loggerPlugin({ level: 'INFO' });
logger('System started'); // [INFO] System started
20. 函数式编程与配置管理
const createConfig = curry((env, config) => ({
...config,
env,
}));
const devConfig = createConfig('development', {
apiUrl: 'http://localhost:3000',
debug: true,
});
console.log(devConfig); // { env: 'development', apiUrl: 'http://localhost:3000', debug: true }