JavaScript、TypeScript 装饰器 作者:马育民 • 2024-11-22 18:31 • 阅读:10017 需要掌握: - [JavaScript 中 call()、apply()、bind()用法](https://www.malaoshi.top/show_1GW5Zx3xSTv.html "JavaScript 中 call()、apply()、bind()用法") - [javascript ...剩余参数(相当于java的不定长参数、可变参数)](https://www.malaoshi.top/show_1GW5XOQt9MX.html "javascript ...剩余参数(相当于java的不定长参数、可变参数)") # 作用 在 **不改动源代码** 的情况下,增加 **额外 功能** ### 应用范围 可以用在 **类**,**属性**,**类中的方法** 或 **方法参数** 上 **注意:**不能修饰函数 ### 理解 与 python的装饰器 很相似 其功能与 java的注解 类似,但运行原理不同 ### 应用场景 - 打印日志 - 性能测试 - 权限校验等 - 数据库事务处理 ### 实现原理 本质上是闭包 # 支持情况 ### 浏览器、Node.js 在当前的 **浏览器** 或 **Node.js** 版本中,**尚不支持装饰器** 因此我们使用使用 `Babel` 插件 `@babel/plugin-proposal-decorators` 来将它转换为 浏览器 或 Node.js 中可识别的代码。 ### typescript 支持装饰器 # 方法装饰器 ### 普通代码 下面例子中,创建 `User` 类,里面有 `login()` 登录方法。 在开发时,经常会测试方法的 **执行效率**,看是否符合公司要求,在该方法中,**两次获取时间**,**进行相减**,计算出登录的 **耗时** 操作 ``` class User { login(username:string,password:string):Object{ const start = Date.now() console.log("用户名:",username) console.log("密码:",password) console.log("模拟处理登录操作") const end = Date.now() console.log("耗时:",(end-start),"毫秒") return { code:0, message:'登录成功!' } } } const user = new User() const res = user.login("lilei","123456") ``` ### 缺点 - 由于 测试耗时的代码,与 登录业务的代码不相关,从规范上讲,不应该混到一起 - 测试耗时的代码,当符合要求后,是要删除的,改动代码后,还要再次进行测试,因为防止 **无意中改动关键代码 导致 报错**,这样就增加工作量 ### 解决 使用 **装饰器**,将 测试耗时的代码 放到 装饰器中 ### 定义装饰器 ``` function test_time(target: any, context: any) { /* 注意:此处第一个参数必须是 this 否则 login() 方法中 this.age 将获取不到 值 */ return function (this:any , ...args: any[] ){ console.log(`开始执行:${context.name}方法`) const start = Date.now() // 通过 call() 方法改变 login() 方法中的this const res = target.call(this , ...args) const end = Date.now() console.log(`结束执行:${context.name}方法,耗时${end-start}毫秒`) return res } } ``` ##### 解释 - target:此处表示修饰的方法 - context:上下文,有以下属性。 - kind:值固定为字符串method,表示当前为方法装饰器。 - name:所装饰的方法名,类型为字符串或 Symbol 值。 - static:布尔值,表示是否为静态方法。该属性为只读属性。 - private:布尔值,表示是否为私有方法。该属性为只读属性。 - access:对象,包含了方法的存取器,但是只有get()方法用来取值,没有set()方法进行赋值。 - addInitializer():为方法增加初始化函数。 ### 使用装饰器 ``` class User { age:number = 20 @test_time login(username:string,password:string):Object{ console.log("age",this.age) console.log("用户名:",username) console.log("密码:",password) return { code:0, message:'登录成功!' } } } ``` 执行结果: ``` 始执行:login方法 age 20 用户名: lilei 密码: 123456 结束执行:login方法,耗时1毫秒 结果: { code: 0, message: '登录成功!' } ``` ### 本质 方法装饰器会改写类的原始方法,实质等同于替代所装饰的函数 ``` class User { age:number = 20 login(username:string,password:string):Object{ console.log(`开始执行:login方法`) const start = Date.now() console.log("age",this.age) console.log("用户名:",username) console.log("密码:",password) const end = Date.now() console.log(`结束执行:login方法,耗时${end-start}毫秒`) return { code:0, message:'登录成功!' } } } ``` 参考: https://zhuanlan.zhihu.com/p/189960001 https://typescript.p6p.net/typescript-tutorial/decorator.html 原文出处:http://www.malaoshi.top/show_1GW5aZMFoTv.html