javascript闭包怎样实现装饰器模式

来源:IPIPP.com作者:缅甸程序员头衔:程序员
导读:本期聚焦于小伙伴创作的《javascript闭包怎样实现装饰器模式》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《javascript闭包怎样实现装饰器模式》有用,将其分享出去将是对创作者最好的鼓励。

在javascript开发中,我们经常需要在不修改原有函数核心逻辑的情况下,为其添加额外的功能,比如执行前后打印日志、缓存函数返回结果、校验调用权限等。装饰器模式恰好满足这个需求,而javascript的闭包特性可以完美支撑装饰器模式的实现,让我们可以动态地为函数扩展能力。

javascript闭包怎样实现装饰器模式

闭包与装饰器模式基础

闭包的核心特性

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的常见方式就是在一个函数内部创建另一个函数。闭包可以记住并访问其词法作用域,即使这个作用域已经执行完毕。简单来说,闭包让函数可以携带其创建时的环境信息,这也是它能实现装饰器的基础。

装饰器模式的定义

装饰器模式属于结构型设计模式,它的核心思想是不改变原有对象的自身结构,通过附加的方式为其添加额外的职责。在javascript中,我们通常用装饰器来增强函数的功能,装饰后的函数会保留原有函数的核心逻辑,同时新增我们定义的功能。

用闭包实现装饰器模式的核心思路

实现的核心逻辑可以分为三步:

  • 定义一个装饰器函数,这个函数接收需要被增强的原始函数作为参数
  • 在装饰器函数内部返回一个新的函数,这个新函数就是增强后的函数
  • 新函数内部先执行我们新增的额外逻辑,再调用原始函数,也可以根据需求调整调用顺序,或者修改原始函数的参数、返回值

因为闭包的特性,返回的新函数可以访问到装饰器函数作用域中的原始函数,所以即使装饰器函数执行完毕,新函数依然可以正常调用原始函数,这就是闭包支撑装饰器实现的关键。

常见场景实现示例

为函数添加执行日志

很多时候我们需要在函数执行前后打印日志,记录函数的调用时间和参数,用闭包装饰器可以很方便地实现:

// 日志装饰器函数
function withLog(fn) {
    // 返回的新函数就是增强后的函数,闭包可以访问fn
    return function(...args) {
        console.log(`函数开始执行,参数:${JSON.stringify(args)},时间:${new Date().toLocaleString()}`);
        // 调用原始函数,保留原有逻辑
        const result = fn.apply(this, args);
        console.log(`函数执行结束,返回值:${JSON.stringify(result)},时间:${new Date().toLocaleString()}`);
        return result;
    };
}

// 原始的业务函数
function add(a, b) {
    return a + b;
}

// 用装饰器增强原始函数
const addWithLog = withLog(add);

// 调用增强后的函数
addWithLog(1, 2);

上面的代码中,withLog是装饰器函数,它接收原始函数add,返回的新函数通过闭包访问到了fn(也就是原始函数),在执行前后添加了日志逻辑,没有修改add函数本身的代码。

实现函数结果缓存

对于计算成本较高的函数,我们可以用装饰器为其添加缓存能力,相同参数的调用直接返回缓存结果,避免重复计算:

// 缓存装饰器函数
function withCache(fn) {
    // 用对象存储缓存结果,闭包让这个缓存对象可以一直被访问
    const cache = {};
    return function(...args) {
        // 把参数作为缓存的键
        const key = JSON.stringify(args);
        // 如果缓存中有结果,直接返回
        if (cache.hasOwnProperty(key)) {
            console.log('从缓存中获取结果');
            return cache[key];
        }
        // 没有缓存则调用原始函数计算
        const result = fn.apply(this, args);
        // 把结果存入缓存
        cache[key] = result;
        return result;
    };
}

// 原始的计算函数,模拟耗时计算
function heavyCalculate(num) {
    console.log('执行耗时计算');
    // 模拟计算耗时
    let total = 0;
    for (let i = 0; i < 100000000; i++) {
        total += i;
    }
    return total + num;
}

// 增强后的带缓存的计算函数
const cachedCalculate = withCache(heavyCalculate);

// 第一次调用,会执行计算
cachedCalculate(10);
// 第二次调用相同参数,直接从缓存返回
cachedCalculate(10);

添加权限校验逻辑

如果某些函数需要校验调用权限,比如只有登录用户才能调用,也可以用装饰器实现:

// 模拟用户登录状态
let isLogin = false;

// 权限校验装饰器
function withAuth(fn) {
    return function(...args) {
        if (!isLogin) {
            console.log('请先登录再操作');
            return;
        }
        // 已登录则执行原始函数
        return fn.apply(this, args);
    };
}

// 原始的需要权限的操作函数
function deleteItem(id) {
    console.log(`删除id为${id}的项目`);
    return true;
}

// 增强后的带权限校验的函数
const authDelete = withAuth(deleteItem);

// 未登录时调用
authDelete(1);
// 修改登录状态后调用
isLogin = true;
authDelete(1);

装饰器的通用化封装

如果有多个不同的装饰逻辑,我们可以把装饰器的创建过程再抽象一层,让使用更方便:

// 通用装饰器创建函数,接收装饰逻辑
function createDecorator(decorateLogic) {
    return function(fn) {
        return function(...args) {
            // 执行装饰逻辑,装饰逻辑可以决定什么时候调用原始函数,或者修改参数返回值
            return decorateLogic.call(this, fn, args);
        };
    };
}

// 用通用装饰器创建日志装饰器
const withLogDecorator = createDecorator(function(fn, args) {
    console.log('函数开始执行');
    const result = fn.apply(this, args);
    console.log('函数执行结束');
    return result;
});

// 用通用装饰器创建缓存装饰器
const withCacheDecorator = createDecorator(function(fn, args) {
    const key = JSON.stringify(args);
    if (!this.cache) {
        this.cache = {};
    }
    if (this.cache.hasOwnProperty(key)) {
        return this.cache[key];
    }
    const result = fn.apply(this, args);
    this.cache[key] = result;
    return result;
});

// 原始函数
function multiply(a, b) {
    return a * b;
}

// 组合多个装饰器,先加缓存再加日志
const enhancedMultiply = withLogDecorator(withCacheDecorator(multiply));
enhancedMultiply(3, 4);
enhancedMultiply(3, 4);

注意事项

  • 装饰器返回的新函数的this指向需要注意,最好用apply或者call来调用原始函数,保证this和原始调用场景一致
  • 如果原始函数有函数名、长度等属性,装饰后的函数会丢失这些属性,需要的话可以手动把这些属性复制到新函数上
  • 不要过度使用装饰器,简单的功能直接修改函数可能更易读,装饰器适合需要复用的增强逻辑场景

javascript闭包装饰器模式函数增强修改时间:2026-06-15 10:36:21

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。