如何实现带参和无参装饰器?能否实现兼容两种用法的@Controller?
没问题,我来一步步给你拆解这几个装饰器的实现方式,用TypeScript举例会更直观,毕竟类装饰器在TS的后端框架(比如NestJS)里用得特别多~
1. 不带参数的装饰器实现
不带参数的装饰器本质就是一个直接接收目标类作为参数的函数。你可以在这个函数里修改类的原型、添加属性/方法,甚至返回一个新的类来包装原类,实现增强逻辑。
举个例子:
// 不带参数的类装饰器 function Controller(target: Function) { // 给类的原型添加属性 target.prototype.controllerName = "DefaultController"; // 返回一个新类,包装原类(可选,用来扩展逻辑) return class extends target { constructor() { super(); console.log(`初始化${target.name}控制器`); } }; } // 无参使用方式 @Controller class SomeClass {} const instance = new SomeClass(); // 控制台输出:初始化SomeClass控制器 console.log(instance.controllerName); // 输出:DefaultController
2. 带参数的装饰器实现
带参数的装饰器其实是一个装饰器工厂函数——它先接收你传入的参数,然后返回真正的装饰器函数(也就是那个接收目标类的函数)。这样就能根据不同的参数给类添加不同的配置或逻辑。
例子如下:
// 带参数的装饰器工厂 function Controller(path: string) { // 返回真正的类装饰器函数 return function(target: Function) { target.prototype.path = path; // 根据路径自动生成控制器名称 target.prototype.controllerName = path.split("/").pop() || "Controller"; }; } // 带参使用方式 @Controller("/api/users") class UserController {} const userInstance = new UserController(); console.log(userInstance.path); // 输出:/api/users console.log(userInstance.controllerName); // 输出:users
3. 支持无参/带参的@Controller装饰器实现
当然可行!核心思路是判断传入的第一个参数类型:
- 如果参数是
Function类型,说明是无参调用(此时参数就是目标类),直接执行装饰器逻辑; - 如果参数是其他类型(比如字符串),就把它当作配置参数,返回装饰器函数。
具体实现:
// 兼容无参/带参的@Controller装饰器 function Controller(arg?: string | Function): any { // 情况1:无参调用,arg就是目标类 if (typeof arg === "function") { const target = arg; target.prototype.path = "/default"; target.prototype.controllerName = "DefaultController"; return target; } // 情况2:带参调用,arg是传入的路径(默认给个兜底值) const path = arg || "/default"; return function(target: Function) { target.prototype.path = path; target.prototype.controllerName = path.split("/").pop() || "Controller"; }; } // 无参使用 @Controller class HomeController {} // 带参使用 @Controller("/api/posts") class PostController {} // 测试效果 const homeInstance = new HomeController(); console.log(homeInstance.path); // 输出:/default console.log(homeInstance.controllerName); // 输出:DefaultController const postInstance = new PostController(); console.log(postInstance.path); // 输出:/api/posts console.log(postInstance.controllerName); // 输出:posts
补充个小提示:如果用TypeScript,记得在tsconfig.json里开启"experimentalDecorators": true,不然装饰器语法会报错哦~
内容的提问来源于stack exchange,提问作者yantrab
相关产品推荐
相关产品推荐

