函数式编程技术

惰性求值

延迟计算直到需要时才执行。

惰性求值概念

// 惰性求值:只在需要时计算
// 避免不必要的计算

// 立即求值
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函子

// 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函子

// 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作为单子

// 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和always

// identity: 返回自身
const identity = x => x;

// 使用
[1, 2, 3].filter(identity);  // 过滤假值

// always: 返回常量
const always = x => () => x;

const alwaysHello = always("Hello");
console.log(alwaysHello());  // "Hello"

flip和negate

// 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和debounce

// 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);
        }
    };
}

下一步

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

  1. 错误处理 - 学习错误处理最佳实践
  2. 调试技术 - 学习调试技巧
  3. 性能优化基础 - 学习性能优化

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

🎯 东巴文寄语:函数式编程技术包括惰性求值、记忆化、函子和单子等高级概念,掌握这些技术可以编写更优雅、更高效的代码。在 db-w.cn,我们帮你深入函数式编程世界!