延迟计算直到需要时才执行。
// 惰性求值:只在需要时计算
// 避免不必要的计算
// 立即求值
const arr = [1, 2, 3, 4, 5];
const result = arr
.map(x => x * 2)
.filter(x => x > 5)
.slice(0, 2);
// 遍历数组3次
// 惰性求值
function* lazyMap(iterable, fn) {
for (const item of iterable) {
yield fn(item);
}
}
function* lazyFilter(iterable, fn) {
for (const item of iterable) {
if (fn(item)) yield item;
}
}
function* lazyTake(iterable, n) {
let count = 0;
for (const item of iterable) {
if (count++ >= n) break;
yield item;
}
}
// 使用
const lazyResult = lazyTake(
lazyFilter(
lazyMap([1, 2, 3, 4, 5], x => x * 2),
x => x > 5
),
2
);
console.log([...lazyResult]); // [6, 8]
class LazyChain {
constructor(iterable) {
this.iterable = iterable;
this.operations = [];
}
map(fn) {
this.operations.push({ type: "map", fn });
return this;
}
filter(fn) {
this.operations.push({ type: "filter", fn });
return this;
}
take(n) {
this.operations.push({ type: "take", n });
return this;
}
*[Symbol.iterator]() {
let current = this.iterable;
for (const op of this.operations) {
switch (op.type) {
case "map":
current = this.*mapGen(current, op.fn);
break;
case "filter":
current = this.*filterGen(current, op.fn);
break;
case "take":
current = this.*takeGen(current, op.n);
break;
}
}
yield* current;
}
*mapGen(iter, fn) {
for (const item of iter) yield fn(item);
}
*filterGen(iter, fn) {
for (const item of iter) {
if (fn(item)) yield item;
}
}
*takeGen(iter, n) {
let count = 0;
for (const item of iter) {
if (count++ >= n) break;
yield item;
}
}
toArray() {
return [...this];
}
}
// 使用
const result = new LazyChain([1, 2, 3, 4, 5])
.map(x => x * 2)
.filter(x => x > 5)
.take(2)
.toArray();
console.log(result); // [6, 8]
缓存函数结果。
// 记忆化:缓存计算结果
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;
};
}
// 使用
const fibonacci = memoize(function(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
console.log(fibonacci(40)); // 快速计算
// 单参数版本更高效
function memoizeOne(fn) {
const cache = new Map();
return function(arg) {
if (cache.has(arg)) {
return cache.get(arg);
}
const result = fn.call(this, arg);
cache.set(arg, result);
return result;
};
}
// 使用
const expensiveCalc = memoizeOne(x => {
console.log("计算中...");
return x * x;
});
console.log(expensiveCalc(5)); // 计算中... 25
console.log(expensiveCalc(5)); // 25(缓存)
函数式编程的容器类型。
// Functor: 可映射的容器
class Functor {
constructor(value) {
this.value = value;
}
map(fn) {
return new Functor(fn(this.value));
}
static of(value) {
return new Functor(value);
}
}
// 使用
const result = Functor.of(2)
.map(x => x + 1)
.map(x => x * 2);
console.log(result.value); // 6
// Maybe: 处理空值
class Maybe {
constructor(value) {
this.value = value;
}
static of(value) {
return new Maybe(value);
}
isNothing() {
return this.value === null || this.value === undefined;
}
map(fn) {
if (this.isNothing()) {
return Maybe.of(null);
}
return Maybe.of(fn(this.value));
}
getOrElse(defaultValue) {
return this.isNothing() ? defaultValue : this.value;
}
}
// 使用
const result1 = Maybe.of(5)
.map(x => x + 1)
.map(x => x * 2)
.getOrElse(0);
console.log(result1); // 12
const result2 = Maybe.of(null)
.map(x => x + 1)
.map(x => x * 2)
.getOrElse(0);
console.log(result2); // 0
// Either: 处理错误
class Either {
static left(value) {
return new Left(value);
}
static right(value) {
return new Right(value);
}
}
class Left {
constructor(value) {
this.value = value;
}
map(fn) {
return this; // 左值不执行
}
getOrElse(defaultValue) {
return defaultValue;
}
getOrElseThrow() {
throw this.value;
}
}
class Right {
constructor(value) {
this.value = value;
}
map(fn) {
return Either.right(fn(this.value));
}
getOrElse(defaultValue) {
return this.value;
}
getOrElseThrow() {
return this.value;
}
}
// 使用
function divide(a, b) {
if (b === 0) {
return Either.left(new Error("除数不能为0"));
}
return Either.right(a / b);
}
const result = divide(10, 2)
.map(x => x * 2)
.getOrElse(0);
console.log(result); // 10
const error = divide(10, 0)
.map(x => x * 2)
.getOrElse(0);
console.log(error); // 0
具有flatMap能力的函子。
// Monad: 可展平的函子
class Monad {
constructor(value) {
this.value = value;
}
static of(value) {
return new Monad(value);
}
map(fn) {
return Monad.of(fn(this.value));
}
flatMap(fn) {
return fn(this.value);
}
}
// 使用
const result = Monad.of(2)
.flatMap(x => Monad.of(x + 1))
.flatMap(x => Monad.of(x * 2));
console.log(result.value); // 6
// Promise是异步单子
const result = Promise.resolve(2)
.then(x => Promise.resolve(x + 1))
.then(x => Promise.resolve(x * 2));
result.then(console.log); // 6
// 链式调用
fetch("/api/user")
.then(response => response.json())
.then(user => fetch(`/api/posts/${user.id}`))
.then(response => response.json())
.then(posts => console.log(posts));
常用的函数式工具函数。
// identity: 返回自身
const identity = x => x;
// 使用
[1, 2, 3].filter(identity); // 过滤假值
// always: 返回常量
const always = x => () => x;
const alwaysHello = always("Hello");
console.log(alwaysHello()); // "Hello"
// flip: 翻转前两个参数
const flip = fn => (a, b, ...rest) => fn(b, a, ...rest);
const subtract = (a, b) => a - b;
const subtractFlipped = flip(subtract);
console.log(subtract(10, 5)); // 5
console.log(subtractFlipped(10, 5)); // -5
// negate: 取反谓词
const negate = fn => (...args) => !fn(...args);
const isEven = x => x % 2 === 0;
const isOdd = negate(isEven);
console.log(isEven(2)); // true
console.log(isOdd(2)); // false
// once: 只执行一次
function once(fn) {
let called = false;
let result;
return function(...args) {
if (!called) {
called = true;
result = fn.apply(this, args);
}
return result;
};
}
const init = once(() => console.log("初始化"));
init(); // 初始化
init(); // 无输出
// debounce: 防抖
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// throttle: 节流
function throttle(fn, delay) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) {
lastTime = now;
fn.apply(this, args);
}
};
}
掌握了函数式编程技术后,让我们继续学习:
东巴文(db-w.cn) - 让编程学习更简单
🎯 东巴文寄语:函数式编程技术包括惰性求值、记忆化、函子和单子等高级概念,掌握这些技术可以编写更优雅、更高效的代码。在 db-w.cn,我们帮你深入函数式编程世界!