js如何获取原型链上的装饰器方法

你无法直接获取装饰器函数本身,因为装饰器在定义时执行并修改目标,运行时只能通过元数据获取其留下的信息。1. 装饰器的作用是修改类或方法的描述符,并在执行时将元数据附加到目标上;2. 使用 reflect.definemetadata 在装饰器中存储信息,如日志消息或权限角色;3. 通过 reflect.getmetadata 从原型链上的方法获取元数据,即使子类继承未覆盖的方法,也能访问父类方法的元数据;4. 若子类重写并重新装饰同名方法,则该方法拥有独立的元数据,原父类元数据需通过父类原型访问;5. 实际应用包括权限控制、api文档生成、依赖注入等,框架可通过读取元数据实现声明式配置。最终结论是:装饰器本身不可获取,但其产生的元数据可通过 reflect api 在原型链上有效读取和利用。

js如何获取原型链上的装饰器方法

在JavaScript中,如果你想“获取”原型链上的装饰器方法,直接的答案是:你无法在运行时直接“获取”到应用在方法上的装饰器函数本身。装饰器是在代码定义阶段执行的,它们的作用是修改类的定义、方法描述符或属性描述符。一旦它们执行完毕,它们就“消失”了,留下的只是它们对目标所做的修改。因此,真正要获取的是装饰器所留下的信息,也就是元数据(metadata)。这通常通过

Reflect.metadata

API 来实现。

js如何获取原型链上的装饰器方法

解决方案

要获取原型链上的装饰器方法所关联的信息,最标准且推荐的做法是让装饰器在执行时将相关数据作为元数据附加到目标对象上。随后,你可以利用

Reflect.getMetadata

API 来检索这些信息。

以下是一个实际的例子,展示了如何创建一个简单的装饰器来附加元数据,以及如何从原型链上的方法中读取它:

js如何获取原型链上的装饰器方法

// 确保 Reflect-metadata polyfill 已导入,例如:// import 'reflect-metadata';/** * 一个简单的日志装饰器,同时附加元数据 * @param {string} message - 要记录的消息 */function LogAndMetadata(message: string) {  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {    // 原始方法    const originalMethod = descriptor.value;    // 附加元数据到方法上    Reflect.defineMetadata('logMessage', message, target, propertyKey);    Reflect.defineMetadata('decoratedBy', 'LogAndMetadata', target, propertyKey);    // 修改方法以添加日志功能    descriptor.value = function (...args: any[]) {      console.log(`[${message}] Calling ${propertyKey} with:`, args);      return originalMethod.apply(this, args);    };    return descriptor;  };}class BaseService {  @LogAndMetadata('Service Method Call')  public doSomething(data: string) {    console.log(`Executing doSomething with: ${data}`);    return `Processed: ${data}`;  }}class ExtendedService extends BaseService {  // 继承了 doSomething 方法,没有覆盖  @LogAndMetadata('Extended Service Init')  public initialize() {    console.log('ExtendedService initialized.');  }}// 尝试获取元数据const baseServiceInstance = new BaseService();baseServiceInstance.doSomething('test'); // 触发日志const extendedServiceInstance = new ExtendedService();extendedServiceInstance.doSomething('another test'); // 触发继承方法的日志console.log('n--- 检查元数据 ---');// 从 BaseService 的原型上获取 doSomething 的元数据const baseLogMessage = Reflect.getMetadata('logMessage', BaseService.prototype, 'doSomething');const baseDecoratorName = Reflect.getMetadata('decoratedBy', BaseService.prototype, 'doSomething');console.log(`BaseService.doSomething - logMessage: ${baseLogMessage}, decoratedBy: ${baseDecoratorName}`);// 从 ExtendedService 的原型上获取 doSomething 的元数据// 即使 ExtendedService 没有直接装饰 doSomething,它也继承了带有元数据的方法const extendedLogMessage = Reflect.getMetadata('logMessage', ExtendedService.prototype, 'doSomething');const extendedDecoratorName = Reflect.getMetadata('decoratedBy', ExtendedService.prototype, 'doSomething');console.log(`ExtendedService.doSomething - logMessage: ${extendedLogMessage}, decoratedBy: ${extendedDecoratorName}`);// 获取 ExtendedService 自己的 initialize 方法的元数据const initLogMessage = Reflect.getMetadata('logMessage', ExtendedService.prototype, 'initialize');console.log(`ExtendedService.initialize - logMessage: ${initLogMessage}`);// 尝试获取一个不存在的元数据const nonExistentMetadata = Reflect.getMetadata('nonExistent', BaseService.prototype, 'doSomething');console.log(`Non-existent metadata: ${nonExistentMetadata}`); // 应该为 undefined

这段代码清晰地展示了,当

ExtendedService

继承

BaseService

doSomething

方法时,由于该方法在

BaseService.prototype

上被装饰并附加了元数据,

ExtendedService.prototype

上的

doSomething

引用的是同一个方法,因此其元数据依然可以通过

Reflect.getMetadata

访问到。

为什么不能直接“获取”装饰器本身?

在我看来,这是一个关于“运行时”与“编译/定义时”边界的问题。装饰器,无论是TypeScript还是Babel实现,它们本质上都是一种元编程工具,在你的代码被编译或加载到运行时环境之前,它们就已经完成了对代码结构的修改。你可以把它想象成一个建筑师:他在设计图纸阶段(代码定义)就决定了房间的布局、窗户的样式(方法的行为、属性的特性)。一旦房子建好了(代码运行),你看到的只是最终的房子,而不是建筑师本人或他当时绘制图纸的过程。

js如何获取原型链上的装饰器方法

所以,当我们谈论“获取装饰器方法”时,我们不是要去抓取那个在定义时执行过的函数引用,因为那个函数已经完成了它的任务。我们真正关心的是,这个“建筑师”在构建过程中留下了什么“标记”或“说明”,这些标记就是元数据。它们是装饰器行为的结果,而不是装饰器本身。这和我们平时使用

Object.defineProperty

类似,你定义了一个属性后,并不能反过来查询是哪个

defineProperty

调用创建了它。装饰器提供了更优雅的语法糖,但底层逻辑依然是属性描述符的操作。

如何在实践中使用 Reflect.metadata 来管理装饰器信息?

在实际项目中,

Reflect.metadata

的应用场景远不止于简单的日志。它提供了一个强大且标准化的机制来为你的类、方法、属性附加任意的上下文信息。

例如,你可以用它来实现:

权限控制: 定义

@Roles('admin')

装饰器,将所需角色信息附加到方法上。在HTTP请求处理时,通过

Reflect.getMetadata('roles', target, methodName)

获取这些角色信息,然后进行权限校验。API文档生成: 创建

@ApiDescription('获取用户列表')

@ApiResponse(UserDto)

等装饰器,将这些信息附加到控制器方法上。在启动时扫描所有控制器,提取这些元数据,自动生成OpenAPI(Swagger)文档。依赖注入: 框架可以利用

@Inject('someService')

装饰器来标记构造函数参数或属性,然后通过反射读取这些元数据,自动解析并注入相应的服务实例。序列化/反序列化: 定义

@Serializable()

@JsonField('custom_name')

装饰器,指示哪些属性应该被序列化,或者在序列化时使用不同的键名。

一个更具体的实践场景是,当你构建一个框架或库时,你希望用户能够通过声明式的方式(即使用装饰器)来配置某些行为。这时,你的框架内部就需要一套机制来读取这些配置。

Reflect.metadata

就是这个机制的核心。

// 假设你有一个简单的路由框架const ROUTE_METADATA_KEY = Symbol('route_path');const METHOD_METADATA_KEY = Symbol('http_method');function Get(path: string) {  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {    Reflect.defineMetadata(ROUTE_METADATA_KEY, path, target, propertyKey);    Reflect.defineMetadata(METHOD_METADATA_KEY, 'GET', target, propertyKey);    // 可以在这里进行其他处理,例如验证路径格式  };}class UserController {  @Get('/users')  public getAllUsers() {    return 'List of all users';  }  @Get('/users/:id')  public getUserById(id: string) {    return `User with ID: ${id}`;  }}// 框架启动时,扫描控制器并注册路由function registerRoutes(controller: any) {  const prototype = controller.prototype;  const methods = Object.getOwnPropertyNames(prototype).filter(name => name !== 'constructor');  console.log(`n--- 注册 ${controller.name} 的路由 ---`);  for (const methodName of methods) {    const routePath = Reflect.getMetadata(ROUTE_METADATA_KEY, prototype, methodName);    const httpMethod = Reflect.getMetadata(METHOD_METADATA_KEY, prototype, methodName);    if (routePath && httpMethod) {      console.log(`注册路由: [${httpMethod}] ${routePath} -> ${controller.name}.${methodName}`);      // 实际框架中,这里会调用 express/koa 等框架的路由注册方法    }  }}registerRoutes(UserController);

通过这种方式,你的框架代码完全不必关心装饰器是如何实现的,它只需要知道如何通过

Reflect.getMetadata

来查询由装饰器留下的“标记”即可。这大大提高了代码的解耦性和可维护性。

继承场景下,原型链上的装饰器元数据如何表现?

理解装饰器元数据在继承链上的行为,关键在于理解JavaScript的原型链本身。当一个方法被装饰时,无论是在类定义还是方法定义上,元数据都是附加到该方法所在的原型对象上的。

例如,如果你有一个

BaseClass

并在其原型上定义了一个被装饰的方法

doWork

BaseClass.prototype.doWork

-> 拥有元数据。

SubClass extends BaseClass

并且

SubClass

没有覆盖

doWork

方法时,

SubClass.prototype.doWork

实际上就是

BaseClass.prototype.doWork

的引用。它们指向的是同一个函数对象。因此,当你通过

Reflect.getMetadata('someKey', SubClass.prototype, 'doWork')

去查询时,你实际上是在查询

BaseClass.prototype.doWork

这个函数对象上的元数据,所以你能够成功获取到。

这正是我们前面示例中

ExtendedService

能够获取到

doSomething

方法元数据的原因。

但如果

SubClass

覆盖

doWork

方法:

class BaseService {  @LogAndMetadata('Base Service Method Call')  public doSomething(data: string) { /* ... */ }}class OverridingService extends BaseService {  @LogAndMetadata('Overriding Service Method Call') // 这里覆盖并重新装饰了  public doSomething(data: string) {    console.log(`OverridingService is doing something with: ${data}`);    // super.doSomething(data); // 可以选择调用父类方法    return `Overridden: ${data}`;  }}// 获取 OverridingService.prototype.doSomething 的元数据const overridingLogMessage = Reflect.getMetadata('logMessage', OverridingService.prototype, 'doSomething');console.log(`nOverridingService.doSomething - logMessage: ${overridingLogMessage}`);// 此时会输出 'Overriding Service Method Call',而不是 'Base Service Method Call'

在这种情况下,

OverridingService.prototype.doSomething

是一个全新的函数对象,它拥有自己独立的元数据。父类的元数据依然存在于

BaseService.prototype.doSomething

上,但子类的方法不再指向它。所以,如果你想获取父类被覆盖方法上的元数据,你需要直接查询父类的原型。

理解这一行为对于设计可扩展的、基于装饰器的系统至关重要。它确保了继承的灵活性,同时允许子类通过覆盖和重新装饰来定义自己的行为和元数据。

以上就是js如何获取原型链上的装饰器方法的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1516015.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
js如何获取所有原型链上的属性
上一篇 2025年12月20日 09:44:10
js如何实现模态框
下一篇 2025年12月20日 09:44:25

相关推荐

  • java中的field有什么用 字段field的3个访问控制技巧

    java中的field有什么用 字段field的3个访问控制技巧java中的field有什么用 字段field的3个访问控制技巧java中的field有什么用 字段field的3个访问控制技巧java中的field有什么用 字段field的3个访问控制技巧

    java中的field主要用于反射,允许运行时检查和修改类的字段,包括私有字段。具体步骤如下:1. 获取class对象后,使用getfield()或getdeclaredfield()获取field对象,前者用于获取public字段(包括继承的),后者用于获取本类声明的所有字段;2. 使用setac…

    2026年8月25日 用户投稿
    000
  • 云原生(Kubernetes)适配进展

    kubernetes的适配进展主要体现在:1) 生态系统的扩展,涌现了如istio和linkerd等工具;2) 与云服务的集成,如gke和eks的托管服务;3) 对新兴技术的支持,如knative的无服务器平台。尽管面临复杂性和安全性挑战,kubernetes仍是云原生技术的领导者。 云原生(Kub…

    2026年8月25日
    000
  • 抖音可以添加什么小程序

    抖音支持接入多种类型的小程序,涵盖电商、游戏、教育、工具等多个领域,让用户在不离开应用的情况下即可完成购物、娱乐、学习和日常事务处理。本文将全面解析抖音可添加的小程序类别及其核心功能,助力用户更高效地使用平台资源。 电商类小程序电商类小程序在抖音生态中占据重要地位,极大提升了内容与消费之间的转化效率…

    2026年8月25日
    000
  • 悟空搜索能搜到公共交通_悟空搜索出行路线规划技巧

    答案是未正确使用查询功能。需在悟空搜索输入起点终点并添加“公交”等关键词,更新应用、开启定位,进入出行标签后选择公交模式,再通过地图视图查看实时信息。 如果您尝试使用悟空搜索查询出行路线,但未能获取公共交通信息,可能是由于查询方式或功能设置未正确使用。以下是解决此问题的步骤: 一、确认搜索关键词与功…

    2026年8月25日
    000
  • 电脑提示DirectX错误导致玩不了游戏怎么办 4种实用方法

    电脑提示DirectX错误导致玩不了游戏怎么办 4种实用方法电脑提示DirectX错误导致玩不了游戏怎么办 4种实用方法电脑提示DirectX错误导致玩不了游戏怎么办 4种实用方法电脑提示DirectX错误导致玩不了游戏怎么办 4种实用方法

    directx是windows平台上运行游戏和图形应用的关键技术组件。当启动游戏时出现“directx错误”“缺少dx11/12”等提示,可能导致程序闪退、画面异常或无法正常运行。以下是几种有效的解决方式。 方法1:更新或修复DirectX组件 DirectX 12等新版组件通常随系统更新一并发布。…

    2026年8月25日 用户投稿
    000
  • 高德地图App如何模拟导航功能 高德地图App提前熟悉路况的练习方法

    可通过高德地图模拟导航预演行车路线。在iPhone 15 Pro上打开高德地图,搜索目的地后点击开始导航进入界面,上滑调出工具栏并点击模拟导航图标即可启动;也可在路线规划页输入起终点后上滑点击播放图标快速模拟;模拟过程中可调节0.5至2倍速或暂停分段练习。 如果您想在出发前预演行车路线,避免实际驾驶…

    2026年8月25日
    100
  • 如何在Laravel应用中快速集成用户消息系统?使用cmgmyr/messenger轻松实现!

    可以通过一下地址学习composer:学习地址 告别从零开始的痛苦:Laravel 消息系统开发的挑战 想象一下,你正在开发一个社交平台或一个团队协作工具,用户之间需要进行私聊、群聊,甚至多方会话。如果你决定从头开始构建这个消息系统,你很快就会发现这远比想象中复杂: 数据库设计: 如何存储会话(Th…

    用户投稿 2026年8月25日
    000
  • 空中版高德地图“空中高德”发布,低空飞行器也能用导航

    感谢网友 sp_ce 提供的线索! 7 月 30 日讯,由龙岗区人民政府携手高德软件有限公司共同主办的“空中高德・龙岗启航”—— 深圳市龙岗区空中高德时空底座发布会今日在香港中文大学(深圳)隆重召开。标志着覆盖全业务场景的低空经济示范项目——“空中高德”在龙岗正式落地启动。 据高德地图官方消息,作为…

    2026年8月25日
    000
  • 作业帮如何查找同类型题目练习_作业帮同类型题目练习指南

    想在作业帮上找到同类型的数学题来巩固练习,关键在于用好它的智能搜索和系统化学习功能。直接拍题搜答案只是第一步,真正提分要靠对同类题型的归纳和训练。下面这几个方法结合使用效果最好。 拍照搜题后查看“同类题”推荐 这是最直接的方式。打开作业帮App,使用首页的“拍照搜题”功能,清晰拍摄你遇到的题目。识别…

    2026年8月25日
    000
  • 电脑屏幕分辨率怎么调 快速设置指南

    电脑屏幕分辨率怎么调 快速设置指南电脑屏幕分辨率怎么调 快速设置指南电脑屏幕分辨率怎么调 快速设置指南电脑屏幕分辨率怎么调 快速设置指南

    合适的分辨率能带来更清晰、细腻的视觉体验,有助于提升工作效率或增强游戏沉浸感。但如果分辨率设置不合理,可能会造成画面模糊、比例异常等问题。本文将详细讲解如何正确调整电脑屏幕分辨率,并分享一些关键的注意事项。 一、屏幕分辨率的基本概念 屏幕分辨率指的是屏幕上显示的像素总数,通常以“横向像素×纵向像素”…

    2026年8月25日 用户投稿
    000
  • 大型综合商城商城与抖音合作的有哪些

    近年来,越来越多的大型综合商城选择与抖音这一热门社交平台联手,借助短视频内容吸引更多消费者关注。这种跨界合作实现了资源互补和互利共赢:商城获得更广泛的流量入口和品牌曝光机会,而抖音则通过引入优质商业内容增强平台生态活力。接下来,本文将深入剖析几个典型合作案例,并探讨此类合作背后的深层价值。 大型综合…

    2026年8月25日
    000
  • Java中Feign的用法 详解声明式调用

    Java中Feign的用法 详解声明式调用Java中Feign的用法 详解声明式调用Java中Feign的用法 详解声明式调用Java中Feign的用法 详解声明式调用

    feign 是一个声明式的 web 服务客户端,它允许开发者像调用本地方法一样调用远程服务。1. feign 的核心优势在于声明式调用,通过定义接口并使用注解即可自动生成实现类;2. 使用 feign 需要添加依赖、启用 feign 客户端并注入 feign 接口;3. 常用注解包括 @feignc…

    2026年8月25日 用户投稿
    000
  • 如何优化Laravel应用的加载速度?

    要优化laravel应用的加载速度,可以从以下几个方面入手:1. 使用缓存来存储变化不频繁但访问频繁的数据,如用户权限;2. 优化数据库查询,避免n+1查询问题,并使用索引加速查询;3. 通过php artisan optimize命令优化代码加载速度;4. 调整服务器配置,使用nginx或apac…

    2026年8月25日
    000
  • Win10系统出现停止代码内存管理错误如何解决?

    win10系统如何应对停止码内存管理问题?内存管理是如何工作的?内存管理负责监控和调控计算机的内存资源,合理地为正在运行的应用程序分配内存块,从而提升整体系统性能。鉴于内存管理在操作系统中的关键作用,一旦出现问题,可能会引发蓝屏死机。有位朋友提到,在遇到win10系统停止代码内存管理错误时不知所措,…

    2026年8月25日
    100
  • ai绘画免费图生图在线生成

    免费在线AI绘画图生图工具推荐: whee:www.whee.com boardmixAI:boardmix.cn boardmix AI整合了丰富多样的AI智能体,涵盖办公效率提升、数据分析报告、文案创作、AI绘图、虚拟角色互动等多个应用场景。每个智能体都具备专业化、垂直化的功能,能够自动执行特定…

    2026年8月25日
    000
  • 在抖音等公域流量平台中怎么有效留存客户数据?

    在抖音等公域流量平台中,想要高效留存客户数据,可以采取以下策略: 借助有赞微商城实现引流转化:有赞微商城集成了丰富的营销功能与数据分析能力,能够帮助商家从抖音等外部流量平台吸引用户并沉淀客户数据。通过在短视频或直播中植入微商城跳转链接,引导用户进入私域商城浏览商品,不仅能提升成交率,还能收集用户的访…

    2026年8月25日
    100
  • win11 浏览器工具栏如何打开

    win11 浏览器工具栏如何打开win11 浏览器工具栏如何打开win11 浏览器工具栏如何打开win11 浏览器工具栏如何打开

    当我们使用win11系统开启浏览器时,如果发现自己常用的工具栏消失不见,却无论如何都找不到它的踪迹,那么接下来就让我们一起来看看如何在win11中重新开启edge浏览器的工具栏吧。以下是具体的操作步骤: 首先,进入win11系统的桌面界面。 接着,点击屏幕左下角的开始菜单,从那里启动我们的edge浏…

    2026年8月25日 用户投稿
    000
  • 电脑主机按了启动键没反应怎么办 详细教程分享

    电脑主机按了启动键没反应怎么办 详细教程分享电脑主机按了启动键没反应怎么办 详细教程分享电脑主机按了启动键没反应怎么办 详细教程分享电脑主机按了启动键没反应怎么办 详细教程分享

    一、确认是否真正“无法启动” 在判断电脑完全无法启动前,先排除是否只是显示问题或启动延迟。可通过以下方式初步排查: 1. 检查显示器供电状态:查看显示器电源灯是否亮起,确认电源线和视频线连接稳固,必要时更换线材或接口测试。 2. 监听主机内部声音:注意是否有风扇运转声、硬盘读写声等。若有声响但无显示…

    2026年8月25日 用户投稿
    000
  • ThinkPHP应用性能瓶颈分析与定位

    性能瓶颈是导致thinkphp应用性能下降的关键点。定位这些瓶颈的方法包括:1.使用xdebug进行性能分析;2.优化数据库查询,使用explain语句分析执行计划;3.优化模板渲染,减少逻辑处理或使用静态化技术;4.应用缓存机制,使用redis或memcached缓存查询结果。 在探索ThinkP…

    2026年8月25日
    000
  • Java中断点的作用 解析调试技巧

    Java中断点的作用 解析调试技巧Java中断点的作用 解析调试技巧Java中断点的作用 解析调试技巧Java中断点的作用 解析调试技巧

    java中断点的作用是暂停程序执行以便调试。它允许检查变量值、单步执行代码,并理解程序运行流程。设置方法:在ide中点击代码行左侧即可添加中断点。管理方法包括启用/禁用单个或全部中断点。高级用法有:1.条件中断点,满足特定条件时暂停程序;2.异常中断点,抛出指定异常时暂停。高效调试技巧包括合理设置中…

    2026年8月25日 用户投稿
    000

发表回复

登录后才能评论
关注微信