闭包的两种设计模式

发布于:2025-07-18 ⋅ 阅读:(15) ⋅ 点赞:(0)

闭包设计模式

概述

闭包是 JavaScript 中的一个重要概念,它允许内层函数访问外层函数的变量。在实际开发中,闭包经常被用于实现特定的设计模式,主要包括辅助函数模式工厂模式

1. 辅助函数模式(Helper Function Pattern)

基本结构

function mainFunction(params) {
    // 外层变量:状态存储
    let sharedState = initialValue;

    function helperFunction(helperParams) {
        // 内层函数:核心逻辑实现
        // 可以访问和修改 sharedState
    }

    // 调用辅助函数
    helperFunction(params);
    return sharedState;  // 返回处理结果
}

特点分析

  • 目的:代码组织和私有化
  • 生命周期:单次调用期间
  • 状态管理:临时状态,每次调用重新初始化
  • 访问权限:辅助函数无法被外部直接访问
  • 使用场景:递归算法、复杂逻辑分解

实际应用示例

1. 数组扁平化
function flatArr(list) {
    let result = [];

    function flat(currentList) {  // 私有辅助函数
        for (let i = 0; i < currentList.length; i++) {
            if (!Array.isArray(currentList[i])) {
                result.push(currentList[i]);
            } else {
                flat(currentList[i]);  // 递归调用
            }
        }
    }

    flat(list);
    return result;
}

// 使用示例
console.log(flatArr([1, [2, 3], [4, [5, 6]]])); // [1, 2, 3, 4, 5, 6]
2. 深度优先搜索
function dfsCollect(tree) {
    let result = [];

    function dfs(node) {  // 私有辅助函数
        if (!node) return;

        result.push(node.value);
        if (node.children) {
            node.children.forEach(dfs);
        }
    }

    dfs(tree);
    return result;
}
3. 对象深拷贝
function deepClone(obj) {
    let visited = new WeakMap();  // 解决循环引用

    function clone(current) {  // 私有辅助函数
        if (current === null || typeof current !== 'object') {
            return current;
        }

        if (visited.has(current)) {
            return visited.get(current);
        }

        const result = Array.isArray(current) ? [] : {};
        visited.set(current, result);

        for (let key in current) {
            if (current.hasOwnProperty(key)) {
                result[key] = clone(current[key]);
            }
        }

        return result;
    }

    return clone(obj);
}

2. 工厂模式 + 装饰器模式(Factory + Decorator Pattern)

基本结构

function createEnhancedFunction(originalFunction) {
    // 外层变量:持久状态
    const persistentState = {};

    return function enhancedFunction(...args) {
        // 增强逻辑:使用持久状态提供额外功能
        // 调用原函数并返回结果
        return originalFunction(...args);
    };
}

特点分析

  • 目的:增强函数功能,状态管理
  • 生命周期:长期存在,跨多次调用
  • 状态管理:持久状态,每个实例独立维护
  • 访问权限:返回的函数可以被外部调用
  • 使用场景:缓存、节流、防抖、日志记录

实际应用示例

1. 函数缓存(记忆化)
function createCache(fn) {
    const cache = new Map();  // 持久缓存状态

    return function (...args) {  // 增强的函数
        const key = JSON.stringify(args);

        if (cache.has(key)) {
            console.log(`Cache hit for args: ${key}`);
            return cache.get(key);
        }

        console.log(`Cache miss for args: ${key}`);
        const result = fn(...args);
        cache.set(key, result);

        return result;
    };
}

// 使用示例
const fibonacci = createCache(function(n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
});

console.log(fibonacci(10)); // 计算并缓存
console.log(fibonacci(10)); // 从缓存获取
2. 函数节流
function createThrottle(delay) {
    let lastTime = 0;  // 持久时间状态

    return function(fn) {  // 增强的函数
        return function(...args) {
            const now = Date.now();
            if (now - lastTime >= delay) {
                lastTime = now;
                return fn.apply(this, args);
            }
        };
    };
}

// 使用示例
const throttle = createThrottle(1000);
const throttledLog = throttle(console.log);
3. 函数调用计数器
function createCounter(fn) {
    let count = 0;  // 持久计数状态

    return function(...args) {  // 增强的函数
        count++;
        console.log(`函数被调用了 ${count}`);
        return fn.apply(this, args);
    };
}

// 使用示例
const countedAdd = createCounter((a, b) => a + b);
console.log(countedAdd(1, 2)); // 函数被调用了 1 次 \n 3
console.log(countedAdd(3, 4)); // 函数被调用了 2 次 \n 7
4. 函数执行时间统计
function createTimer(fn) {
    const stats = { totalTime: 0, callCount: 0 };  // 持久统计状态

    return function(...args) {  // 增强的函数
        const start = Date.now();
        const result = fn.apply(this, args);
        const end = Date.now();

        stats.totalTime += (end - start);
        stats.callCount++;
        stats.averageTime = stats.totalTime / stats.callCount;

        console.log(`执行时间: ${end - start}ms, 平均时间: ${stats.averageTime}ms`);
        return result;
    };
}

3. 两种模式对比

特性 辅助函数模式 工厂模式 + 装饰器模式
主要目的 代码组织和逻辑分解 功能增强和状态管理
状态生命周期 单次调用期间 跨多次调用持久存在
状态初始化 每次调用重新初始化 创建时初始化,后续复用
函数可见性 辅助函数私有 返回增强后的公开函数
使用方式 直接调用主函数 先创建增强函数,再调用
典型应用 递归算法、复杂计算 缓存、节流、装饰器

4. 选择指南

使用辅助函数模式的场景:

  • 需要将复杂逻辑分解为多个步骤
  • 实现递归算法时需要维护中间状态
  • 希望隐藏实现细节,只暴露主要接口
  • 需要在单次执行中共享临时变量

使用工厂模式 + 装饰器模式的场景:

  • 需要为现有函数添加额外功能
  • 需要维护跨调用的持久状态
  • 实现缓存、节流、防抖等功能增强
  • 需要创建多个具有相似功能但独立状态的函数

5. 常见问题解答(FAQ)

Q: 多个 createCache 实例会造成缓存冲突吗?

A: 不会冲突。每次调用 createCache(fn) 都会创建一个全新的闭包环境,每个返回的函数都有自己独立的 cache 变量。

原理解释
function createCache(fn) {
    const cache = new Map();  // 每次调用createCache都会创建新的cache

    return function (...args) {
        // 这个返回的函数只能访问它所属闭包环境中的cache
        const key = JSON.stringify(args);

        if (cache.has(key)) {
            console.log(`Cache hit for ${fn.name}: ${key}`);
            return cache.get(key);
        }

        console.log(`Cache miss for ${fn.name}: ${key}`);
        const result = fn(...args);
        cache.set(key, result);

        return result;
    };
}
实际验证示例
// 创建两个不同的缓存函数
const cachedAdd = createCache(function add(a, b) {
    return a + b;
});

const cachedMultiply = createCache(function multiply(a, b) {
    return a * b;
});

// 测试:相同参数但不同函数
console.log('=== 第一次调用 ===');
console.log(cachedAdd(2, 3));      // Cache miss for add: [2,3] -> 5
console.log(cachedMultiply(2, 3));  // Cache miss for multiply: [2,3] -> 6

console.log('=== 第二次调用相同参数 ===');
console.log(cachedAdd(2, 3));      // Cache hit for add: [2,3] -> 5
console.log(cachedMultiply(2, 3));  // Cache hit for multiply: [2,3] -> 6

// 结果:每个函数都有自己独立的缓存,互不影响
闭包作用域独立性
// 每次调用 createCache 时的内存模型:
const fn1 = createCache(addFunction);    // 创建闭包环境1,有独立的cache1
const fn2 = createCache(multiplyFunction); // 创建闭包环境2,有独立的cache2

// 内存结构示意:
/*
闭包环境1: {
    cache: Map { [2,3] => 5 }  // 独立的缓存空间1
    return function1
}

闭包环境2: {
    cache: Map { [2,3] => 6 }  // 独立的缓存空间2
    return function2
}
*/

// fn1 只能访问 cache1
// fn2 只能访问 cache2
// 它们完全独立,互不影响
需要注意的场景
// 同一个函数创建多个缓存实例
const fibonacci = function(n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
};

const cache1 = createCache(fibonacci);
const cache2 = createCache(fibonacci);

// 这样会创建两个独立的缓存,可能造成重复计算
console.log(cache1(10)); // 第一个缓存计算
console.log(cache2(10)); // 第二个缓存重新计算(因为缓存是独立的)
最佳实践
// 1. 单例模式:对于同一个函数,通常只创建一个缓存实例
const cachedFibonacci = createCache(function fibonacci(n) {
    if (n <= 1) return n;
    return cachedFibonacci(n - 1) + cachedFibonacci(n - 2);
});

// 2. 带清理功能的缓存
function createCacheWithClear(fn) {
    const cache = new Map();

    const cachedFn = function (...args) {
        const key = JSON.stringify(args);

        if (cache.has(key)) {
            return cache.get(key);
        }

        const result = fn(...args);
        cache.set(key, result);
        return result;
    };

    // 添加清理方法
    cachedFn.clearCache = () => cache.clear();
    cachedFn.getCacheSize = () => cache.size;

    return cachedFn;
}

// 使用示例
const smartCache = createCacheWithClear((x) => x * x);
console.log(smartCache(5)); // 25
console.log(smartCache.getCacheSize()); // 1
smartCache.clearCache();
console.log(smartCache.getCacheSize()); // 0

6. 最佳实践

性能考虑

  • 辅助函数模式:避免在频繁调用的函数中使用复杂的辅助逻辑
  • 工厂模式:注意内存泄漏,合理管理持久状态的生命周期

可读性优化

  • 使用描述性的函数名称
  • 添加适当的注释说明闭包的用途
  • 保持函数职责单一

调试技巧

  • 在开发环境中添加日志输出
  • 使用浏览器开发者工具的断点调试
  • 注意闭包可能导致的内存占用

总结

闭包设计模式是 JavaScript 中强大的编程工具,通过合理运用辅助函数模式和工厂模式,可以写出更加优雅、可维护的代码。关键是要根据具体的使用场景选择合适的模式,并注意性能和内存管理的最佳实践。

核心要点:

  • 每个闭包创建独立的作用域环境
  • 工厂函数返回的每个实例都有独立的状态
  • 合理使用闭包可以实现强大的功能增强
  • 注意内存管理和性能优化

网站公告

今日签到

点亮在社区的每一天
去签到