在TypeScript中,装饰器(Decorators)是一种特殊类型的声明, 它可以附加到类声明,方法,访问器,属性或参数上,并且允许修改或注释这些声明 。装饰器在设计模式和元编程方面提供了一些强大的工具。
以下是一些常见的装饰器用法:
- 类装饰器:类装饰器应用于类声明。它接受一个参数,即类的构造函数。
- 方法装饰器:方法装饰器应用于方法声明。它接受三个参数:目标对象,方法名和方法的描述符。
- 属性装饰器:属性装饰器应用于属性声明。它接受两个参数:目标对象和属性名。
- 参数装饰器:参数装饰器应用于构造函数的参数。它接受三个参数:目标对象,方法名(如果是静态方法则是构造函数本身),和参数在参数列表中的索引。
装饰器可以用于很多场景,例如实现依赖注入、方法调用日志、验证等。
在使用装饰器时,确保你的TypeScript配置启用了experimentalDecorators和emitDecoratorMetadata选项。
experimentalDecorators的作用:
- 启用装饰器的实验性支持。装饰器目前仍然是 ECMAScript 标准的一个提案,因此在 TypeScript 中,你需要将这个选项设置为 true 才能使用装饰器。
emitDecoratorMetadata的作用:
- 在生成装饰器元数据(Decorator Metadata)方面的支持。当启用这个选项时,TypeScript 会为装饰器生成额 外的元数据,以便在运行时进行反射。
装饰器的定义
-
装饰器
- 就是一个函数
- 可以注入到:类、方法、属性、参数、对象
- 扩展其功能
-
装饰器要解决的问题
- 装饰器就是解决在不修改类、属性、方法、参数的时候为其添加额外的功能。
-
高阶组件本质上也采用了装饰器的思想
- 在
Nets.js中装饰器可以解决依赖注入的问题 - 有了依赖注入,能大大降低项目的耦合度,大大提升项目的扩展性
- 在
-
依赖注入核心思想
- 使用和创建分离
装饰器的分类
- 类装饰器
- 属性装饰器
- 方法装饰器
- 参数装饰器
- 元数据的装饰器
装饰器的两种写法
- 使用时不传参
- 装饰器工厂:使用时可以传参
环境搭建
- 安装
ts-node
- 可以使用
ts-node命令直接运行*.ts文件,不需要先编译成*.js再使用node执行
npm install -g ts-node
-
安装
concurrently,支持合并执行,同时运行多个script命令npm i concurrently -snpm i nodemon -s -
新建目录,初始化TS配置,生成
tsconfig.jsontsc --init -
生成Node包管理文件
pakcage.jsonnpm init -
配置文件修改配置支持,修改
tsconfig.json
- 解开装饰器配置
- 支持普通装饰器
- 支持元数据装饰器
- 设置源码目录和编译生成JS代码目录
"experimentalDecorators": true, // 支持普通装饰器
"emitDecoratorMetadata": true, // 支持元数据装饰器
"rootDir": "./src", // 源代码根目录
"outDir": "./dist", // 生成JS代码的根目录
- 配置
package.json
- 监控
dist/teaching目录中的js文件,变化时执行node命令 - 合并启动
- 命令解决
typescript编译装饰器类时出现的bugtsconfig.json中配置装饰器配置这是避免代码报错- 编译执行需要 在指定支持装饰器选项
ts-node命令配置- 这个配置需要根据文件名进行修改,后面会有提示
"dev:build": "tsc -w",
"dev:start": "nodemon --watch dist/decorators js --exec node ./dist/decorators/1ClassDecorator.js",
"start": "concurrently npm:dev:*",
"tsc": "tsc src/decorators/1ClassDecorator.ts -- target ES5 -w --experimentaDecorators",
"ctrl": "ts-node src/controller/HomeController.ts",
"beginapp": "nodemon --watch src/ -e ts --exec ts-node ./src/expressapp.ts"
- 创建
src/decorators
类装饰器
-
不带参数的类装饰器
-
代码
function FirstClassDecorator(targetClass: any) {let targetClassObj = new targetClass()targetClassObj.buy() // 下单购买console.log("targetClass.name: ", targetClass.name) // targetClass.name: CustomerService}@FirstClassDecoratorclass CustomerService {name: string = "下单"constructor() {}buy() {console.log(this.name + "购买")}placeOrder() {console.log(this.name + "下单购买")}}export {} -
package.json配置- 当前代码的文件名
1ClassDecorator.ts - 修改
1ClassDecorator.js为1ClassDecorator.js
{"name": "ts-examples","version": "1.0.0","description": "","scripts": {"dev:build": "tsc -w","dev:start": "nodemon --watch dist/decorators js --exec node ./dist/decorators/1ClassDecorator.js","start": "concurrently npm:dev:*","tsc": "tsc src/decorators/1ClassDecorator.ts -- target ES5 -w --experimentaDecorators","ctrl": "ts-node src/controller/HomeController.ts","beginapp": "nodemon --watch src/ -e ts --exec ts-node ./src/expressapp.ts"},"author": "","license": "ISC","dependencies": {"concurrently": "^8.2.2","nodemon": "^3.0.1"}} - 当前代码的文件名
-
运行代码
npm run start -
JS源码
"use strict";// 1. 底层JS **组合装饰器和目标类** __decorate函数var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {// argsnum 参数个数var argsnum = arguments.length;// targetinfo 被装饰器修饰的目标【本案例为类】// argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]// argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】// argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefinedvar targetinfo = argsnum < 3 ? target :desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;console.log("target: ", target) // target : [Function: CustomerService]console.log("targetinfo: ", targetinfo) // targetinfo: [Function: CustomerService]// 由于无参数,argsnum < 3, 其实是torgetinfo = target// decorators: 保存``装饰器数组``元素// decorators = [FirstClassDecorator]var decorator;// 元数据信息,支持reflect-metadata元数据if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {targetinfo = Reflect.decorate(decorators, target, key, desc);} else// 装饰器循环,倒着循环,说明同一个目标上有多个装饰器,执行顺序是倒着执行for (var i = decorators.length - 1; i >= 0; i--) {if (decorator = decorators[i]) {// 如果参数小于3【decorator为类装饰器或者构造器参数装饰器】执行decorator(targetinfo)直接执行decorator装饰器,并传递目标targetinfo,这里是类// 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo)// 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)// targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo// 这里返回一个新的targetinfo// 是把原来的targetinfo 传入到每一个装饰器函数中// 将CustomerService 传入到装饰器函数中,将执行逻辑交给装饰器内部执行// 如果函数有返回值// - 那么targetinfo就为装饰器的返回值// - 如果装饰器函数无返回值,则执行`|| targetinfo`targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;console.log("targetinforesult:", targetinfo)}}// 参数数目小于三个argsnum < 3, 直接返回targetinforeturn argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;}// 底层JS 组合装饰器和目标类 __decorate函数结束// 不带参数的装饰器function FirstClassDecorator(targetClass) {var targetClassObj = new targetClass();targetClassObj.buy(); // 下单购买console.log("targetClass.name: ", targetClass.name); // targetClass.name: CustomerService}// 被装饰的类var CustomerService = /** @class */ (function () {function CustomerService() {this.name = "下单";}CustomerService.prototype.buy = function () {console.log(this.name + "购买");};CustomerService.prototype.placeOrder = function () {console.log(this.name + "下单购买");};// 调用`__decorate`函数// - 参数// - 装饰器列表// - 类构造函数名称 CustomerServiceCustomerService = __decorate([FirstClassDecorator,__metadata("design:paramtypes", [])], CustomerService);return CustomerService;}())
-
-
带参数的类装饰器
- 代码
function FirstClassDecorator(params: any) {console.log("params out: ", params)return function(targetClass: any) {let targetClassObj = new targetClass()targetClassObj.buy()console.log("targetClass.name: ", targetClass.name)console.log("params in: ", params)}}@FirstClassDecorator("我是用来修饰CustomService类的装饰器参数")class CustomerService {name: string = "下单"constructor() {}buy() {console.log(this.name + "购买")}placeOrder() {console.log(this.name + "下单购买")}}export {}
package.json配置- 当前代码的文件名
2ClassDecorator.ts - 修改
1ClassDecorator.js为2ClassDecorator.js
{"name": "ts-examples","version": "1.0.0","description": "","scripts": {"dev:build": "tsc -w","dev:start": "nodemon --watch dist/decorators js --exec node ./dist/decorators/2ClassDecorator.js","start": "concurrently npm:dev:*","tsc": "tsc src/decorators/1ClassDecorator.ts -- target ES5 -w --experimentaDecorators","ctrl": "ts-node src/controller/HomeController.ts","beginapp": "nodemon --watch src/ -e ts --exec ts-node ./src/expressapp.ts"},"author": "","license": "ISC","dependencies": {"concurrently": "^8.2.2","nodemon": "^3.0.1"}}- 当前代码的文件名
- 运行代码
npm run start
- JS源码
"use strict";// 1. 底层JS **组合装饰器和目标类** __decorate函数var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {// argsnum 参数个数var argsnum = arguments.length;// targetinfo 被装饰器修饰的目标【本案例为类】// argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]// argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】// argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefinedvar targetinfo = argsnum < 3 ? target :desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100console.log("target : ", target) // [Function: CustomerService]console.log("targetinfo: ", targetinfo) // [Function: CustomerService] // argsnum < 3 targetinfo = target// decorators: 保存`` 装饰器的数组``元素// decorators = [FirstClassDecorator]var decorator;// 元数据信息,支持reflect-metadata元数据if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {targetinfo = Reflect.decorate(decorators, target, key, desc);} else// 装饰器循环,倒着循环,说明同一个目标上有多个装饰器,执行顺序是倒着执行for (var i = decorators.length - 1; i >= 0; i--) {if (decorator = decorators[i]) {// 如果参数 小于3【decorator为类装饰器或者构造器参数装饰器】执行decorator(targetinfo)直接执行decorator装饰器,并传递目标targetinfo,这里是类// 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo)// 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)// targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfotargetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;console.log("Final targetinfo result:", targetinfo)}}return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;}// 带参数的装饰器function FirstClassDecorator(params: any) {console.log("params out: ", params)return function(targetClass: any) {let targetClassObj = new targetClass()targetClassObj.buy()console.log("targetClass.name: ", targetClass.name)console.log("params in: ", params)}}// 被装饰的类var CustomerService = /** @class */ (function () {function CustomerService() {this.name = "下单";}CustomerService.prototype.buy = function () {console.log(this.name + "购买");};CustomerService.prototype.placeOrder = function () {console.log(this.name + "下单购买");};// 调用// - 参数// - 装饰器列表// - 类构造函数名称 CustomerService// 和不带参数的区别就是// - 会把带参数装饰器先执行一遍// - 带参数的装饰器,返回的是一个函数作为装饰器函数// - 然后传入被装饰的类CustomerService = __decorate([FirstClassDecorator("我是用来修饰CustomService类的装饰器参数"),], CustomerService);return CustomerService;}());
- 代码
泛型工厂继承装饰器
-
毫无相干的两个类可以相互赋值
- 条件是:属性相同
- 必须拥有我的所有属性(可以多,不能少),才能赋值给我
- 等号右边完全包含左边属性
- 父类类名,可以接受子类的类名(多态)
-
泛型工厂继承装饰器
-
装饰器类作为子类,继承父类的属性和方法
-
返回装饰器子类的类名
-
完成类的功能扩展和替换
-
代码
// 1. 完成日志信息的装饰器function LoggerInfoDecorator<T extends { new(...args: any): any}>(targetClass: T) {class LoggerInfoDecorator extends targetClass {constructor(...args: any) {super(...args)console.log("Logging INFO targetClass: ", (targetClass as any).name)}}return LoggerInfoDecorator}// 2. 目标类@LoggerInfoDecoratorclass Test {name!: stringage!: number// 1. 先执行原来的构造函数constructor(name: string) {this.name = name}eat() {console.log(this.name, "Eating Food")}}let test = new Test("Zhangsan")test.eat()export {} -
JS源码
// 1. 继承源码代码let __extends = (function (Son, Parent) {function getStaticExtendsWithForIn (Son, Parent) {for (let key in Parent) {if (Object.prototype.hasOwnProperty.call(Parent, key)) {Son[key] = Parent[key]}}}function getStaticExtendsWithObjectkeys (Son, Parent) {Object.keys(Parent).forEach((key) => {Son[key] = Parent[key]})}function getStaticExtendsWithProto (Son, Parent) {Son.__proto__ = Parent;}let MyextendStatics = function (Son, Parent) {let MyextendStatics = Object.setPrototypeOf || getStaticExtendsWithForIn ||getStaticExtendsWithObjectkeys || getStaticExtendsWithProtoreturn MyextendStatics(Son, Parent)}return function (Son, Parent) {MyextendStatics(Son, Parent)function Middle () {this.constructor = Son;}if (Parent) {//如果不为空 如果父类存在Middle.prototype = Parent.prototype;Son.prototype = new Middle()} else {// 如果父类不存在Son.prototype = Object.create(null)}console.log("Object.create(null):", Object.create(null));}}())// 2. 底层JS 组合装饰器和目标类 __decorate 函数var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {// argsnum 参数个数var argsnum = arguments.length;// targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】// argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]// argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】// argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefinedvar targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100// decorator保存装饰器数组元素var decorator;// 元数据信息,支持reflect-metadata元数据if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {targetinfo = Reflect.decorate(decorators, target, key, desc);} else// 装饰器循环,倒着循环,说明同一个目标上有多个装饰器,执行顺序是倒着执行for (var i = decorators.length - 1; i >= 0; i--) {if (decorator = decorators[i]) {// 如果参数小于3【decorator为类装饰器或者构造器参数装饰器】执行decorator(targetinfo)直接执行decorator装饰器,并传递目标targetinfo,这里是类// 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo)// 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)// targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfotargetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;console.log("targetinforesult:", targetinfo)}}return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;}// 底层 JS 组合装饰器和目标类 __decorate 函数结束// 2.装饰器类function LoggerInfoDecorator(targetClass) {var LoggerInfoDecorator = /** @class */ (function (_super) {__extends(LoggerInfoDecorator, _super); // ****继承关系****function LoggerInfoDecorator() {var args = [];for (var _i = 0; _i < arguments.length; _i++) {args[_i] = arguments[_i];}var _this = _super.apply(this, args) || this;console.log("Logging INFO targetClass: ", targetClass.name);return _this;}return LoggerInfoDecorator;}(targetClass));return LoggerInfoDecorator;}// 2. 目标类var Test = /** @class */ (function () {// 1. 先执行原来的构造函数function Test(name) {this.name = name;}Test.prototype.eat = function () {console.log(this.name, "Eating Food");};Test = __decorate([LoggerInfoDecorator,], Test);return Test;}());// 4.测试var test = new Test("ok");
-