第4章:函数式编程进阶
📖 本章概述
函数式编程是一种强大的编程范式,强调使用纯函数、不可变数据和函数组合来构建程序。本章将深入探讨JavaScript中函数式编程的高级概念和实践技巧。
🎯 学习目标
完成本章学习后,你将能够:
- 深入理解高阶函数和闭包的高级用法
- 掌握柯里化和偏函数应用技术
- 运用函数组合构建复杂的数据处理管道
- 理解和应用不可变数据结构
- 初步了解Monad等函数式编程概念
🔧 高阶函数与闭包进阶
高阶函数模式
javascript
// 函数工厂模式
function createValidator(rules) {
return function(data) {
const errors = [];
for (const [field, rule] of Object.entries(rules)) {
const value = data[field];
if (rule.required && (value === undefined || value === null)) {
errors.push(`${field} is required`);
continue;
}
if (value !== undefined && rule.type && typeof value !== rule.type) {
errors.push(`${field} must be of type ${rule.type}`);
}
if (rule.min && value.length < rule.min) {
errors.push(`${field} must be at least ${rule.min} characters`);
}
if (rule.max && value.length > rule.max) {
errors.push(`${field} must be at most ${rule.max} characters`);
}
if (rule.pattern && !rule.pattern.test(value)) {
errors.push(`${field} format is invalid`);
}
}
return {
isValid: errors.length === 0,
errors
};
};
}
// 使用示例
const userValidator = createValidator({
username: {
required: true,
type: 'string',
min: 3,
max: 20,
pattern: /^[a-zA-Z0-9_]+$/
},
email: {
required: true,
type: 'string',
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
},
age: {
required: false,
type: 'number'
}
});
const result = userValidator({
username: 'john_doe',
email: 'john@example.com',
age: 25
});
console.log(result); // { isValid: true, errors: [] }
装饰器模式实现
javascript
// 函数装饰器
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log('Cache hit for:', key);
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
function timing(fn) {
return function(...args) {
const start = performance.now();
const result = fn.apply(this, args);
const end = performance.now();
console.log(`${fn.name} executed in ${end - start}ms`);
return result;
};
}
function retry(maxAttempts = 3, delay = 1000) {
return function(fn) {
return async function(...args) {
let lastError;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn.apply(this, args);
} catch (error) {
lastError = error;
console.log(`Attempt ${attempt} failed:`, error.message);
if (attempt < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError;
};
};
}
// 组合装饰器
function compose(...decorators) {
return function(fn) {
return decorators.reduceRight((decorated, decorator) => {
return decorator(decorated);
}, fn);
};
}
// 使用示例
const expensiveCalculation = compose(
timing,
memoize,
retry(3, 500)
)(function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
console.log(expensiveCalculation(40));
🍛 柯里化与偏函数应用
柯里化实现
javascript
// 通用柯里化函数
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return function(...nextArgs) {
return curried.apply(this, args.concat(nextArgs));
};
};
}
// 自动柯里化装饰器
function autoCurry(fn) {
function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
}
return (...nextArgs) => curried(...args, ...nextArgs);
}
return curried;
}
// 实用的柯里化函数
const add = curry((a, b, c) => a + b + c);
const multiply = curry((a, b) => a * b);
const filter = curry((predicate, array) => array.filter(predicate));
const map = curry((transform, array) => array.map(transform));
const reduce = curry((reducer, initial, array) => array.reduce(reducer, initial));
// 使用示例
const add5 = add(5);
const add5And3 = add5(3);
console.log(add5And3(2)); // 10
const double = multiply(2);
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.map(double)); // [2, 4, 6, 8, 10]
// 函数式数据处理管道
const isEven = x => x % 2 === 0;
const square = x => x * x;
const sum = (acc, x) => acc + x;
const processNumbers = (numbers) => {
return reduce(sum, 0)(
map(square)(
filter(isEven)(numbers)
)
);
};
console.log(processNumbers([1, 2, 3, 4, 5, 6])); // 56 (2² + 4² + 6²)
偏函数应用
javascript
// 偏函数应用实现
function partial(fn, ...presetArgs) {
return function(...laterArgs) {
return fn(...presetArgs, ...laterArgs);
};
}
// 占位符支持的偏函数
const _ = Symbol('placeholder');
function partialWithPlaceholders(fn, ...args) {
return function(...laterArgs) {
const finalArgs = [];
let laterIndex = 0;
for (let i = 0; i < args.length; i++) {
if (args[i] === _) {
finalArgs[i] = laterArgs[laterIndex++];
} else {
finalArgs[i] = args[i];
}
}
// 添加剩余参数
while (laterIndex < laterArgs.length) {
finalArgs.push(laterArgs[laterIndex++]);
}
return fn(...finalArgs);
};
}
// 使用示例
function greet(greeting, name, punctuation) {
return `${greeting}, ${name}${punctuation}`;
}
const sayHello = partial(greet, 'Hello');
const sayHelloToJohn = partial(sayHello, 'John');
console.log(sayHelloToJohn('!')); // "Hello, John!"
// 使用占位符
const greetJohn = partialWithPlaceholders(greet, _, 'John', '!');
console.log(greetJohn('Hi')); // "Hi, John!"
console.log(greetJohn('Hey')); // "Hey, John!"
🔗 函数组合与管道
函数组合实现
javascript
// 基础组合函数
function compose(...fns) {
return function(value) {
return fns.reduceRight((acc, fn) => fn(acc), value);
};
}
// 管道函数(从左到右)
function pipe(...fns) {
return function(value) {
return fns.reduce((acc, fn) => fn(acc), value);
};
}
// 异步组合
function composeAsync(...fns) {
return function(value) {
return fns.reduceRight(async (acc, fn) => {
const resolved = await acc;
return fn(resolved);
}, Promise.resolve(value));
};
}
// 异步管道
function pipeAsync(...fns) {
return function(value) {
return fns.reduce(async (acc, fn) => {
const resolved = await acc;
return fn(resolved);
}, Promise.resolve(value));
};
}
// 使用示例
const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;
// 组合:从右到左执行
const composedFn = compose(square, double, addOne);
console.log(composedFn(3)); // square(double(addOne(3))) = square(8) = 64
// 管道:从左到右执行
const pipedFn = pipe(addOne, double, square);
console.log(pipedFn(3)); // square(double(addOne(3))) = square(8) = 64
// 异步处理示例
const fetchUser = async (id) => ({ id, name: `User${id}` });
const addTimestamp = async (user) => ({ ...user, timestamp: Date.now() });
const saveToCache = async (user) => {
console.log('Saving to cache:', user);
return user;
};
const processUser = pipeAsync(fetchUser, addTimestamp, saveToCache);
processUser(123).then(console.log);
高级组合模式
javascript
// 条件组合
function when(predicate, fn) {
return function(value) {
return predicate(value) ? fn(value) : value;
};
}
function unless(predicate, fn) {
return when(value => !predicate(value), fn);
}
// 分支组合
function branch(predicate, onTrue, onFalse = x => x) {
return function(value) {
return predicate(value) ? onTrue(value) : onFalse(value);
};
}
// 并行组合
function parallel(...fns) {
return function(value) {
return fns.map(fn => fn(value));
};
}
// 使用示例
const isPositive = x => x > 0;
const isEven = x => x % 2 === 0;
const negate = x => -x;
const half = x => x / 2;
const processNumber = pipe(
when(isPositive, double),
unless(isEven, addOne),
branch(x => x > 10, half, square)
);
console.log(processNumber(3)); // 3 -> 6 -> 7 -> 49
console.log(processNumber(-2)); // -2 -> -2 -> -1 -> 1
// 并行处理
const analyzeNumber = parallel(
x => ({ isPositive: x > 0 }),
x => ({ isEven: x % 2 === 0 }),
x => ({ absolute: Math.abs(x) })
);
console.log(analyzeNumber(-4));
// [{ isPositive: false }, { isEven: true }, { absolute: 4 }]
🔒 不可变数据结构
不可变操作实现
javascript
// 不可变数组操作
const ImmutableArray = {
push: (arr, ...items) => [...arr, ...items],
pop: (arr) => arr.slice(0, -1),
shift: (arr) => arr.slice(1),
unshift: (arr, ...items) => [...items, ...arr],
splice: (arr, start, deleteCount = 0, ...items) => [
...arr.slice(0, start),
...items,
...arr.slice(start + deleteCount)
],
update: (arr, index, value) => [
...arr.slice(0, index),
value,
...arr.slice(index + 1)
],
remove: (arr, index) => [
...arr.slice(0, index),
...arr.slice(index + 1)
]
};
// 不可变对象操作
const ImmutableObject = {
set: (obj, key, value) => ({ ...obj, [key]: value }),
delete: (obj, key) => {
const { [key]: deleted, ...rest } = obj;
return rest;
},
merge: (obj, updates) => ({ ...obj, ...updates }),
update: (obj, key, updater) => ({
...obj,
[key]: updater(obj[key])
}),
setIn: (obj, path, value) => {
if (path.length === 0) return value;
if (path.length === 1) return { ...obj, [path[0]]: value };
const [head, ...tail] = path;
return {
...obj,
[head]: ImmutableObject.setIn(obj[head] || {}, tail, value)
};
},
getIn: (obj, path) => {
return path.reduce((current, key) => current?.[key], obj);
}
};
// 使用示例
const originalArray = [1, 2, 3, 4, 5];
const newArray = ImmutableArray.update(originalArray, 2, 99);
console.log(originalArray); // [1, 2, 3, 4, 5] - 未改变
console.log(newArray); // [1, 2, 99, 4, 5] - 新数组
const originalObject = {
user: {
name: 'John',
profile: {
age: 30,
city: 'New York'
}
}
};
const updatedObject = ImmutableObject.setIn(
originalObject,
['user', 'profile', 'age'],
31
);
console.log(originalObject.user.profile.age); // 30 - 未改变
console.log(updatedObject.user.profile.age); // 31 - 新值
结构共享优化
javascript
// 简单的结构共享实现
class ImmutableList {
constructor(items = []) {
this.items = items;
this.length = items.length;
}
push(item) {
return new ImmutableList([...this.items, item]);
}
pop() {
if (this.length === 0) return this;
return new ImmutableList(this.items.slice(0, -1));
}
get(index) {
return this.items[index];
}
set(index, value) {
if (index < 0 || index >= this.length) {
throw new Error('Index out of bounds');
}
const newItems = [...this.items];
newItems[index] = value;
return new ImmutableList(newItems);
}
map(fn) {
return new ImmutableList(this.items.map(fn));
}
filter(predicate) {
return new ImmutableList(this.items.filter(predicate));
}
reduce(reducer, initial) {
return this.items.reduce(reducer, initial);
}
toArray() {
return [...this.items];
}
[Symbol.iterator]() {
return this.items[Symbol.iterator]();
}
}
// 使用示例
const list1 = new ImmutableList([1, 2, 3]);
const list2 = list1.push(4);
const list3 = list2.map(x => x * 2);
console.log(list1.toArray()); // [1, 2, 3]
console.log(list2.toArray()); // [1, 2, 3, 4]
console.log(list3.toArray()); // [2, 4, 6, 8]
// 链式操作
const result = new ImmutableList([1, 2, 3, 4, 5])
.filter(x => x % 2 === 0)
.map(x => x * x)
.reduce((sum, x) => sum + x, 0);
console.log(result); // 20 (2² + 4²)
🧩 Monad概念入门
Maybe Monad实现
javascript
// Maybe Monad - 处理可能为空的值
class Maybe {
constructor(value) {
this.value = value;
}
static of(value) {
return new Maybe(value);
}
static nothing() {
return new Maybe(null);
}
isNothing() {
return this.value === null || this.value === undefined;
}
map(fn) {
return this.isNothing() ? Maybe.nothing() : Maybe.of(fn(this.value));
}
flatMap(fn) {
return this.isNothing() ? Maybe.nothing() : fn(this.value);
}
filter(predicate) {
return this.isNothing() || !predicate(this.value)
? Maybe.nothing()
: this;
}
getOrElse(defaultValue) {
return this.isNothing() ? defaultValue : this.value;
}
toString() {
return this.isNothing() ? 'Maybe.Nothing' : `Maybe.Just(${this.value})`;
}
}
// 使用示例
const user = {
name: 'John',
address: {
street: '123 Main St',
city: 'New York'
}
};
// 安全的属性访问
function safeGet(obj, path) {
return path.reduce((maybe, key) => {
return maybe.flatMap(value =>
value && typeof value === 'object' && key in value
? Maybe.of(value[key])
: Maybe.nothing()
);
}, Maybe.of(obj));
}
const cityMaybe = safeGet(user, ['address', 'city']);
console.log(cityMaybe.getOrElse('Unknown')); // "New York"
const zipMaybe = safeGet(user, ['address', 'zip']);
console.log(zipMaybe.getOrElse('Unknown')); // "Unknown"
// 链式操作
const result = Maybe.of(' hello world ')
.map(s => s.trim())
.map(s => s.toUpperCase())
.filter(s => s.length > 5)
.map(s => `Result: ${s}`)
.getOrElse('No result');
console.log(result); // "Result: HELLO WORLD"
Either Monad实现
javascript
// Either Monad - 处理可能失败的操作
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;
}
flatMap(fn) {
return this.isRight ? fn(this.value) : this;
}
mapLeft(fn) {
return this.isRight ? this : Either.left(fn(this.value));
}
fold(leftFn, rightFn) {
return this.isRight ? rightFn(this.value) : leftFn(this.value);
}
getOrElse(defaultValue) {
return this.isRight ? this.value : defaultValue;
}
toString() {
return this.isRight
? `Either.Right(${this.value})`
: `Either.Left(${this.value})`;
}
}
// 使用示例
function divide(a, b) {
return b === 0
? Either.left('Division by zero')
: Either.right(a / b);
}
function sqrt(x) {
return x < 0
? Either.left('Square root of negative number')
: Either.right(Math.sqrt(x));
}
// 链式计算
const calculation = divide(16, 4)
.flatMap(result => sqrt(result))
.map(result => result * 2);
console.log(calculation.toString()); // "Either.Right(4)"
const failedCalculation = divide(16, 0)
.flatMap(result => sqrt(result))
.map(result => result * 2);
console.log(failedCalculation.toString()); // "Either.Left(Division by zero)"
// 错误处理
const result = calculation.fold(
error => `Error: ${error}`,
value => `Success: ${value}`
);
console.log(result); // "Success: 4"
📝 本章小结
本章深入探讨了JavaScript函数式编程的高级概念:
- 高阶函数:函数工厂、装饰器等高级模式
- 柯里化:函数参数的部分应用和组合
- 函数组合:构建复杂的数据处理管道
- 不可变性:安全的数据操作和结构共享
- Monad:优雅的错误处理和空值处理
这些技术将帮助你:
- 编写更加模块化和可复用的代码
- 减少副作用和状态管理复杂性
- 构建更加健壮的错误处理机制
- 提高代码的可测试性和可维护性
🚀 下一章预告
下一章我们将学习元编程与反射,探讨Proxy、Reflect、Symbol等JavaScript元编程技术。
继续学习:第5章:元编程与反射