前言
TypeScript 装饰器 | 阮一峰 TypeScript 教程
- 迩来,在进行学习 Nest 后台服务的时候发现,对于 TS 装饰器的用法还是有一些暗昧,于是写一篇文章来详细解说这个概念,目前设置到的知识点如下:
前置知识
类与实例
TypeScript 的 class 类型 | 阮一峰 TypeScript 教程
原型与原型链
- 原型:每一个 JavaScript 对象(null 除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性,其实就是 prototype 对象,主要作用是共享方法
- 原型链:由相互关联的原型构成的链状结构就是原型链
- 以一个经典的类继承举例
- 起首定义一个 Person 类,定义 Teacher 继承 Person 类,然后输出参数构造 Teacher 类的实例 t1
- 则原型链向上查找的过程为 t1 的隐式原型 __proto__ 指向类 Teacher 的显示原型 prototype,以此类推实行父类 Person 的 prototype,父类的 __proto__ 指向 Object 的 prototype。最终 Object 的 __proto__ 指向 null
- instanceof:能否在向上查找的原型链中,找到和目标匹配的显示原型
- hasOwnProperty() 方法返回一个布尔值,表现对象自有属性(而不是继承来的属性)中是否具有指定的属性。
this 指向与绑定
一文搞懂 this 指向-CSDN博客
雷同点:都会改变函数的this指向
绑定不同:
- bind不会改变原函数的this关键字,返回一个新的函数,不会立刻调用
- call、apply都可以立刻调用原函数
传参不同:
- bind、call传入一组参数
- apply传入数组参数
简介
- 装饰器(Decorator):是一种语法结构,在定义时修改类的行为,表现 @ + 表达式
- 表达式必须是一个函数,或者实行后得到一个函数
- 吸收所装饰对象的一些相关值作为参数
- 函数要么修改原有值,要么返回新对象代替修饰的目标对象
- TypeScript 早期支持旧的装饰器语法(后续解说),5.0 后使用尺度语法,传统语法需要打开--experimentalDecorators编译参数
- 不同类型的装饰器可以修改/代替原目标对象的内容
- 结构如下所示:
- type Decorator = (
- value: DecoratedValue, // 所装饰的对象
- context: { // 上下文对象
- kind: string; // 类型,class, method, getter, setter, field, accessor
- name: string | symbol; // 所装饰目标的名称
- addInitializer?(initializer: () => void): void; // 完善类的初始化逻辑
- static?: boolean; // 所装饰的对象是否为类的私有成员
- private?: boolean; // 所装饰的对象是否为类的静态成员
- access: { // 一个对象,包含了某个值的 get 和 set 方法
- get?(): unknown;
- set?(value: unknown): void;
- };
- }
- ) => void | ReplacementValue;
复制代码 类装饰器
结构
- type ClassDecorator = (
- value: Function, // 当前类本身
- context: {
- kind: "class";
- name: string | undefined;
- addInitializer(initializer: () => void): void;
- }
- ) => Function | void;
复制代码 案例
- function Greeter(value: any, context: any) {
- // 代替当前的类
- return class extends value {
- age: number = 18
- greet() {
- console.log('Hello, ' + this.name, this.age)
- }
- }
- // 代替当前的构造方法
- const newConstructor = function (...args: any[]) {
- const instance = new value(...args)
- instance.age = 18
- // 添加新的方法
- instance.greet = function () {
- console.log('Hello, ' + this.name, this.age)
- }
- return instance
- }
- // 复制原型链
- newConstructor.prototype = value.prototype
- return newConstructor
- }
- // @ts-ignore
- @Greeter
- class Person {
- public name: string
- constructor(name: string) {
- this.name = name
- }
- greet() {}
- }
- const person = new Person('John')
- person.greet() // Hello, John
复制代码 方法装饰器
结构
- // 方法装饰器会改写类的原始方法,实际上就是传递方法的代码结构,在基础上进行修改
- type ClassMethodDecorator = (
- value: Function, // 方法体
- context: {
- kind: "method";
- name: string | symbol;
- static: boolean; // 是否为静态方法
- private: boolean; // 是否为只读属性
- access: { get: () => unknown }; // 方法的存取器
- addInitializer(initializer: () => void): void;
- }
- ) => Function | void;
复制代码 案例
- function delay(ms: number) {
- return function (target: any, context: any) {
- return function (...args: any[]) {
- console.log('延时执行', ms)
- setTimeout(() => {
- target.apply(target, args)
- console.log('延时结束')
- }, ms)
- }
- }
- }
- class Person {
- public name: string
- constructor(name: string) {
- this.name = name
- }
- @delay(1000)
- greet() {
- console.log(this.name)
- }
- }
复制代码 属性装饰器
结构
- type ClassFieldDecorator = (
- value: undefined, // 不能通过 value 获取装饰器的值
- context: {
- kind: "field";
- name: string | symbol;
- static: boolean;
- private: boolean;
- access: { get: () => unknown; set: (value: unknown) => void };
- addInitializer(initializer: () => void): void;
- }
- ) => (initialValue: unknown) => unknown | void;
复制代码 案例
- function fieldDecorator(target: any, context: any) {
- const { kind, name } = context
- if (kind === 'field') {
- return function (value: string) {
- console.log(`Decorating ${name} with value ${value}`)
- return value
- }
- }
- }
- function twice(target: any, context: any) {
- return (value: number) => value * 2
- }
- class Person {
- @fieldDecorator
- public name: string = 'John Doe'
- @twice
- public age: number = 30
- }
- const person = new Person()
- console.log(person.name)
- console.log(person.age)
复制代码 getter、setter 装饰器
结构
- type ClassGetterDecorator = (
- value: Function,
- context: {
- kind: "getter";
- name: string | symbol;
- static: boolean;
- private: boolean;
- access: { get: () => unknown };
- addInitializer(initializer: () => void): void;
- }
- ) => Function | void;
- type ClassSetterDecorator = (
- value: Function,
- context: {
- kind: "setter";
- name: string | symbol;
- static: boolean;
- private: boolean;
- access: { set: (value: unknown) => void };
- addInitializer(initializer: () => void): void;
- }
- ) => Function | void;
复制代码 案例
- function lazy(target: any, { kind, name }: any) {
- if (kind === 'getter') {
- // 返回函数,这个函数会在实例上定义一个与装饰器目标同名的属性
- return function (this: any) {
- const result = target.call(this)
- Object.defineProperty(this, name, {
- value: result,
- writable: false,
- })
- return result
- }
- }
- }
- class C {
- @lazy
- get value() {
- console.log('正在计算……')
- return '开销大的计算结果'
- }
- }
- const inst = new C()
- console.log(inst.value) // 正在计算…… 开销大的计算结果
- console.log(inst.value) // 开销大的计算结果
复制代码 accessor 装饰器
结构
- type ClassAutoAccessorDecorator = (
- value: { // 将变量变为私有属性,并自动添加 get/set 方法,静态属性和私有属性都可用
- get: () => unknown;
- set: (value: unknown) => void;
- },
- context: {
- kind: "accessor";
- name: string | symbol;
- access: { get(): unknown; set(value: unknown): void };
- static: boolean;
- private: boolean;
- addInitializer(initializer: () => void): void;
- }
- ) => {
- get?: () => unknown;
- set?: (value: unknown) => void;
- init?: (initialValue: unknown) => unknown; // 可以取代初始变量的值
- } | void;
复制代码 案例
- function logAccess(value: any, context: any) {
- const { get, set } = value
- return {
- get() {
- const result = get.call(this)
- console.log(`Getting ${String(context.name)}: ${result}`)
- return result
- },
- set(newValue: number) {
- console.log(`Setting ${String(context.name)} to ${newValue}`)
- set.call(this, newValue)
- },
- init(initValue: number) {
- console.log(`Initializing ${String(context.name)} with ${initValue}`)
- return initValue + 1 // 返回初始化值
- },
- }
- }
- class Student {
- @logAccess
- accessor age: number = 0
- }
- const student = new Student()
- console.log(student.age) // Getting age: 1
- student.age = 20 // Setting age to 20;
复制代码 装饰器实行顺序
- function d(str: string) {
- console.log(`评估 @d(): ${str}`)
- return (value: any, context: any) => console.log(`应用 @d(): ${str}`)
- }
- function log(str: string) {
- console.log(str + ' 被调用')
- return str
- }
- @d('类装饰器')
- class T {
- @d('静态属性装饰器')
- static staticField = log('静态属性值')
- @d('实例属性装饰器')
- instanceField = log('实例属性值')
- @d('静态方法装饰器')
- static staticMethod() {
- return log('静态方法')
- }
- @d('实例方法装饰器 2')
- @d('实例方法装饰器 1')
- [log('instanceMethod')]() {
- return log('实例方法')
- }
- @d('getter 装饰器')
- get getter() {
- return log('getter')
- }
- @d('setter 装饰器')
- set setter(value: any) {
- log('setter')
- }
- }
- const t = new T()
- console.log(t.instanceField)
- // 阶段一:评估
- 评估 @d(): 类装饰器
- 评估 @d(): 静态属性装饰器
- 评估 @d(): 实例属性装饰器
- 评估 @d(): 静态方法装饰器
- 评估 @d(): 实例方法装饰器 2
- 评估 @d(): 实例方法装饰器 1
- instanceMethod 被调用
- 评估 @d(): getter 装饰器
- 评估 @d(): setter 装饰器
- // 阶段二:应用
- 应用 @d(): 静态方法装饰器
- 应用 @d(): 实例方法装饰器 1
- 应用 @d(): 实例方法装饰器 2
- 应用 @d(): getter 装饰器
- 应用 @d(): setter 装饰器
- 应用 @d(): 静态属性装饰器
- 应用 @d(): 实例属性装饰器
- 应用 @d(): 类装饰器
- 静态属性值 被调用
- // 阶段三:实例执行
- 实例属性值 被调用
- 实例属性值
复制代码
- 评估阶段(evaluation):计算 @ 符号反面表达式的值,确保是函数
- 评估阶段的实行,从上到小实行
- 碰到目标是函数的返回的属性,需要实行返回结果才行
- 应用阶段(application):评估之后得到的函数,应用到所装饰目标
- 多个装饰器的,从下到上依次实行
- 静态方法 => 实例方法 => getter => setter => 静态属性 => 实例属性 => 类装饰器
- 静态属性值由函数返回的,此时实行
- 实例属性值由函数返回的,实例调用才实行
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |