TypeScript 装饰器定义

目录
  • 1.概念
    • 1.1定义
    • 1.2装饰器工厂
    • 1.3装饰器组合使用
    • 1.4装饰器求值
  • 2.类装饰器
  • 3.方法装饰器
  • 4.访问器装饰器
  • 5.属性装饰器
  • 6.参数装饰器

前言:

装饰器Decorator ECMAScript中已经提案,但是目前还没有定案;在TypeScript中已经将其实现,但是这仍是一项正在试验中的特性,如果想要使用装饰器,需要在tsconfig.json中将experimentalDecorators属性,将其设置为true。

1.概念

1.1定义

装饰器是一种新的声明,它可以作用于类声明 、方法 、访问器 、属性 以及参数 上。装饰器的使用采用@符号加一个函数名称,例如@testDecorator,其中,这个testDecorator必须是一个函数或者 return一个函数 ,这个函数在运行的时候被调用,被装饰的声明作为参数会自动传入。

值得注意的是 ,装饰器要紧挨着要修饰的内容的前面 ,而且所有的装饰器不能用在声明文件.d.ts.中,和任何外部上下文中(比如declare)。

装饰器的定义以及使用如下所示:

// 定义一个函数作为装饰器函数使用
function testDecorator() {}

// 通过@符号使用装饰器
@testDecorator

1.2装饰器工厂

所谓的装饰器工厂也是一个函数,与普通的装饰器函数不同的是它的返回值是一个函数,返回的函数作为装饰器调用的函数。如果使用装饰器工厂,可以在使用的时候根据当前的使用情况,传递不同的参数,但是在使用的时候,就需要加上函数调用。

示例代码如下:

// 装饰器工厂,返回值是一个函数
function testDecorator() {
    return function() {}
}

// 通过@符号 + 函数调用的方式使用装饰器
@testDecorator()

1.3装饰器组合使用

装饰器是可以组合使用的,也就是说可以对用一个目标,引用多个装饰器,

示例代码如下所示:

// 定义两个装饰器函数
function setName() {}
function setAge() {}

// 使用装饰器
@setName
@setAge
class Person {}

如果使用多个装饰器,装饰器的执行是有顺序的,执行顺序如下:

如果使用的普通的装饰器函数的话,执行顺序是从下往上执行的,

示例代码如下:

function setName(constructor: any) {
  console.log('setName', constructor)
}
function setAge(constructor: any) {
  console.log('setAge', constructor)
}
@setName
@setAge
class Person {}
/* 执行结果如下:
setAge [Function: Person]
setName [Function: Person]
*/

如果是装饰器工厂的,它的执行顺序是先从上到下依次执行工厂函数,然后从下往上依次执行工厂函数return的函数。示例代码如下

function setName() {
  console.log('get setName')
  return function (constructor: any) {
    console.log('setName', constructor)
  }
}
function setAge() {
  console.log('get setAge')
  return function (constructor: any) {
    console.log('setAge', constructor)
  }
}
@setName()
@setAge()
class Person {}
/* 执行结果如下:
get setName
get setAge
setAge [Function: Person]
setName [Function: Person]
*/

1.4装饰器求值

类的定义中不同声明上的装饰器将按以下规定的顺序引用:

  • 参数装饰器,方法装饰器,访问符装饰器或属性装饰器应用到每个实例成员;
  • 参数装饰器,方法装饰器,访问符装饰器或属性装饰器应用到每个静态成员;
  • 参数装饰器应用到构造函数;
  • 类装饰器应用到类。

2.类装饰器

类装饰器 在类声明之前使用,必须紧挨着需要装饰的内容,类装饰器应用于类的声明。

类装饰器表达式会在运行时当做函数被调用,它有一个参数,就是这个类的构造函数。

示例代码如下:

let sign = null
function setName() {
  return function (constructor: Function) {
    sign = constructor
  }
}
@setName()
class Info {
  constructor() {}
}
console.log(sign === Info) // true
console.log(sign === Info.prototype.constructor) // true

如上代码可以知道类Info的原型对象的constructor属性指向的其实就是Info本身。

我们还可以通过装饰器来修改类的原型对象和构造函数,示例代码如下:

// * 通过装饰器 修改原型对象与构造函数
function addName(constructor: { new (): any }) {
  constructor.prototype.name = '一碗周'
}
@addName
class Person {}
const person = new Person()
console.log(person.name) // error 类型“A”上不存在属性“name”

在上面的代码中,我们通过addName修饰符在类Person的原型上添加一个name属性,这样使得通过Person类实例化的对象,都可以访问name这个属性,但是实际上并不是这样的,这里已经抛出一个异常,想要解决这个问题,可以通过类型断言的方式,也可以通过定义一个同名接口,通过声明合并的方式解决这个问题。

示例代码如下:

function addName(constructor: { new (): any }) {
  constructor.prototype.name = '一碗周'
}
@addName
class Person {}
const person = new Person()
// 1. 类型断言
// console.log((person as any).name) // 一碗周

// 2. 定义同名接口,声明合并
interface Person {
  name: string
}

console.log(person.name) // 一碗周

而且我们还可以通过装饰器重载构造函数,示例代码如下:

// * 重载构造函数
function classDecorator<T extends { new (...args: any[]): {} }>(
  constructor: T,
) {
  return class extends constructor {
    name = '一碗周'
    hobby = 'coding'
  }
}
@classDecorator
class Person {
  age = 18
  name: string
  constructor(name: string) {
    this.name = name
  }
}
const person = new Person('一碗周')
console.log(person)
/* 执行结果如下:
{
  age: 18,
  name: '一碗周',
  hobby: 'coding',
}
*/

我们还可以通过装饰器工厂的方式来传递参数,示例代码如下:

// 定义一个装饰器工厂
function classDecorator(_name: string) {
  return function <T extends { new (...args: any[]): {} }>(constructor: T) {
    return class extends constructor {
      name = _name
      hobby = 'coding'
    }
  }
}
@classDecorator('一碗周')
class Person {
  age = 18
  name: string
  constructor(name: string) {
    this.name = name
  }
}
const person = new Person('一碗粥')
console.log(person)
/* 执行结果如下:
{
  age: 18,
  name: '一碗周',
  hobby: 'coding',
}
*/

3.方法装饰器

方法装饰器用来处理类中的方法,它可以处理方法的属性描述符(关于什么是属性描述符,请参考Object.defineProperty() ),也可以处理方法定义。方法装饰器在运行时也是被当做函数调用,其包含三个参数,

具体如下所示:

对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。

成员的名字。

成员的属性描述符 。

值得注意的是如果代码输出目标版本小于ES5,属性描述符 将会是undefined

如下代码通过装饰器工厂定义了一个简单的方法装饰器,示例代码如下:

// 装饰器工厂
function enumerable(bool: boolean) {
  /**
   * 方法装饰器接受三个参数:
   * 1. target:对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
   * 2. propertyName:成员的名字
   * 3. descriptor:属性描述符,其类型为 PropertyDescriptor
   */
  return function (
    target: any,
    propertyName: string,
    descriptor: PropertyDescriptor,
  ) {
    // 根据传入的bool决定该方法是否可枚举
    descriptor.enumerable = bool
  }
}
class Info {
  constructor(public name: string) {}
  @enumerable(false)
  getName() {
    return this.name
  }
}
const info = new Info('一碗周')
// 如果直接打印,该对象中不包含 getName() 方法,因为该方法是不可枚举的。
console.log(info) // { name: '一碗周' }
// 但是可以调用该方法
console.log(info.getName()) // 一碗周

在上面的代码中,我们直接通过装饰器对类中的方法的属性描述符进行了修改。

如果方法装饰器返回一个值,那么会用这个值作为方法的属性描述符对象,示例代码如下:

// 装饰器工厂
function enumerable(bool: boolean) {
  return function (
    target: any,
    propertyName: string,
    descriptor: PropertyDescriptor,
  ) {
    return {
      value: function () {
        return 'Error: name is undefined'
      },
      enumerable: bool,
    }
  }
}
class Info {
  constructor(public name: string) {}
  @enumerable(false)
  getName() {
    return this.name
  }
}
const info = new Info('一碗周')
console.log(info) // { name: '一碗周' }
console.log(info.getName()) // Error: name is undefined

在上面的代码中,我们的方法装饰器中返回了一个对象,该对象的value属性修改了方法的定义,所以最终看到的结果为Error: name is undefined

4.访问器装饰器

访问器装饰器就是之前所学习的setget方法,一个在设置属性值的时候触发,一个在获取属性值的时候触发。

访问器装饰器同样也接受三个参数,与方法装饰器一样,这里不做赘述了,

示例代码如下:

function enumerable(bool: boolean) {
  return function (
    target: any,
    propertyName: string,
    descriptor: PropertyDescriptor,
  ) {
    descriptor.enumerable = bool
  }
}
class Info {
  private _name: string
  constructor(name: string) {
    this._name = name
  }
  @enumerable(false)
  get name() {
    return this._name
  }
  set name(name) {
    this._name = name
  }
}

值得注意的是,在TypeScript不允许同时装饰一个成员的getset访问器。

5.属性装饰器

属性装饰器声明在属性声明之前,它有两个参数,如下所示:

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 成员的名字。

示例代码如下:

function printPropertyName(target: any, propertyName: string) {
  console.log(propertyName)
}
class Info {
  @printPropertyName
  name: string
  @printPropertyName
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}
new Info('一碗周', 18)

执行结果如下:

name
age

6.参数装饰器

参数装饰器具有三个参数,具体如下:

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 成员的名字。
  • 参数在函数参数列表中的索引。

参数装饰器的作用是用于监视一个方法的参数是否被传入,参数装饰器的返回值会被忽略。

示例代码如下:

function required(target: any, propertyName: string, index: number) {
  console.log(`修饰的是${propertyName}的第${index + 1}个参数`)
}
class Info {
  name: string = '一碗周'
  age: number = 18
  getInfo(prefix: string, @required infoType: string): any {
    return prefix + ' ' + this[infoType]
  }
}
interface Info {
  [key: string]: string | number | Function
}
const info = new Info()
info.getInfo('', 'age') // 修饰的是getInfo的第2个参数

这里我们在getInfo方法的第二个参数之前使用参数装饰器,从而可以在装饰器中获取到一些信息。

到此这篇关于TypeScript 装饰器定义的文章就介绍到这了,更多相关TypeScript 装饰器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解JavaScript私有类字段和TypeScript私有修饰符

    JavaScript私有类字段和隐私需求 在过去,JavaScript 没有保护变量不受访问的原生机制,当然除非是典型闭包. 闭包是 JavaScript 中许多类似于私有模式(如流行的模块模式)的基础.但是,近年来 ECMAScript 2015 类被使用后,开发人员感到需要对类成员的隐私进行更多控制. 类字段提案(在撰写本文时处于第 3 阶段)试图通过引入私有类字段来解决问题. 让我们看看它们是什么样子的. 一个 JavaScript 私有类字段的例子 这是一个带有私有字段的 JavaScr

  • TypeScript的函数定义与使用案例教程

    TypeScript中函数的定义和使用 1. 声明一个函数约束其传参类型,以及返回值类型 传入两个参数,没有返回值 const fun1 = (key: string, value: number): void => { console.log(key, value);//"Typescript",100 }; fun1("Typescript", 100); 2.TypeScript中的函数配置可选参数,在ES5或者ES6中函数中的实参可以不传递进去,但是在

  • TypeScript定义接口(interface)案例教程

    接口的作用: 接口,英文:interface,其作用可以简单的理解为:为我们的代码提供一种约定. 在Typescript中是这么描述的: TypeScript的核心原则之一是对值所具有的结构进行类型检查.它有时被称做"鸭式辨型法"或"结构性子类型化". 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约. 举个例子: // 定义接口 Person interface Person { name: string; age: numb

  • 简单了解TypeScript中如何继承 Error 类

    前言 在JavaScript 中很多时候都需要自定义错误,尤其是开发 Node.js 应用的时候. 比如一个典型的网站服务器可能需要有 NetworkError, DatabaseError, UnauthorizedError 等. 我们希望这些类都拥有 Error 的特性:有错误消息.有调用栈.有方便打印的 toString 等. 最直观的实现方式便是 继承 Error 类. 但考虑 TypeScript 需要编译到 ES5 兼容性问题会较为复杂, 本文用来帮助理解 TypeScript 中

  • TypeScript 学习笔记之 typeScript类定义,类的继承,类成员修饰符

    目录 1.类的定义 2.类的继承 3.修饰符 前言: typeScript 中的类与 ES6 中的类非常相似,如果不知道 ES6 中的类,建议先学习下 ES6 中的 class .本篇文章重点介绍 typeScript 中的类定义.继承以及修饰符. 1.类的定义 类描述了所创建的对象共同的属性和方法.typeScript 支持面向对象的所有特性,比如类.接口等. 在 typeScript 中定义类的时候,使用 class 关键字,类名首字母使用大写,类可以包含以下三个模块: ​字段​ - 字段是

  • TypeScript 装饰器定义

    目录 1.概念 1.1定义 1.2装饰器工厂 1.3装饰器组合使用 1.4装饰器求值 2.类装饰器 3.方法装饰器 4.访问器装饰器 5.属性装饰器 6.参数装饰器 前言: 装饰器Decorator 在ECMAScript中已经提案,但是目前还没有定案:在TypeScript中已经将其实现,但是这仍是一项正在试验中的特性,如果想要使用装饰器,需要在tsconfig.json中将experimentalDecorators属性,将其设置为true. 1.概念 1.1定义 装饰器是一种新的声明,它可

  • JS装饰者模式和TypeScript装饰器

    装饰者模式介绍 装饰者模式(Decorator Pattern)也称为装饰器模式,在不改变对象自身的基础上,动态增加额外的职责.属于结构型模式的一种. 使用装饰者模式的优点:把对象核心职责和要装饰的功能分开了.非侵入式的行为修改. 举个例子来说,原本长相一般的女孩,借助美颜功能,也能拍出逆天的颜值.只要善于运用辅助的装饰功能,开启瘦脸,增大眼睛,来点磨皮后,咔嚓一拍,惊艳无比. 经过这一系列叠加的装饰,你还是你,长相不增不减,却能在镜头前增加了多重美.如果你愿意,还可以尝试不同的装饰风格,只要装

  • vue中typescript装饰器的使用方法超实用教程

    VueConf ,尤大说, Vue 支持 Ts 了,网上关于 Vue + Ts 的资料有点少, 楼主踩了一个星期坑,终于摸明白了 修饰器 的玩法,下面我们就来玩下 Vue 的 decorator 吧 1,data 值的声明 在这里 public 声明的是公有属性, private 声明的是私有属性,私有属性要带 下划线 蓝色框里的内容是声明组件,在每个组件创建时都要带上, Components 中的写法如下 上面是 普通写法 ,下面是 懒加载写法 2.@Prop 父组件传值给子组件 父组件使用

  • Python如何将装饰器定义为类

    问题 你想使用一个装饰器去包装函数,但是希望返回一个可调用的实例. 你需要让你的装饰器可以同时工作在类定义的内部和外部. 解决方案 为了将装饰器定义成一个实例,你需要确保它实现了 __call__() 和 __get__() 方法. 例如,下面的代码定义了一个类,它在其他函数上放置一个简单的记录层: import types from functools import wraps class Profiled: def __init__(self, func): wraps(func)(self

  • VUE中使用TypeScript装饰器实现表单验证的全过程

    目录 前言 装饰器 class-validator 封装Validator 具体使用 小结 前言 最近接触了关于很多TypeScript装饰器的知识,以及class-validator这个用装饰器来做表单验证的包,就萌生了想在vue中使用装饰器来做表单验证的想法.class-validator允许我们在类上通过使用装饰器来完成表单的验证,并且可在浏览器端和node端同时使用.那么接下来先简单介绍一下装饰器和class-validator的用法. 装饰器 装饰器的语法十分简单,只需要在想使用的装饰

  • Typescript装饰器AOP示例详解

    目录 在Typescript中使用装饰器 配置 类装饰器 方法装饰器 AOP(面向切面编程) 在Typescript中使用装饰器 上文中讲了装饰模式,今天来来介绍一些Typescript里面的装饰器,以及如何用装饰器来实现之前提及装饰模式,装饰器只是实现装饰模式的一种方式,并非唯一 配置 在Typescript要使用装饰器需要在tsconfig打开装饰器的语法 "compilerOptions": { "experimentalDecorators": true }

  • Vue框架TypeScript装饰器使用指南小结

    前言 装饰器是一种特殊类型的声明,它能够被附加到 类声明,方法, 访问符,属性或参数 上. 装饰器使用 @expression这种形式, expression求值 后必须为一个函数,它会在 运行时被调用 ,被装饰的声明信息做为参数传入. 本篇先从项目的宏观角度来总结一下Decorator如何组织. 目录 主要的Decorator依赖 vue-class-component vuex-class vue-property-decorator core-decorators 自定义Decorator

  • TypeScript中的装饰器用法

    一.装饰器 装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为. 通俗的讲装饰器就是一个方法,可以注入到类.方法.属性参数上来扩展类.属性.方法.参数的功能. 常见的装饰器有:类装饰器.属性装饰器.方法装饰器.参数装饰器 装饰器的写法:普通装饰器(无法传参) . 装饰器工厂(可传参) 装饰器是过去几年中js最大的成就之一,已是Es7的标准特性之一 二.类装饰器: 类装饰器在类声明之前被声明(紧靠着类声明). 类装饰器应用于类构造函数,可以用来监视,修改或替换类

  • 实例讲解Python编程中@property装饰器的用法

    取值和赋值 class Actress(): def __init__(self): self.name = 'TianXin' self.age = 5 类Actress中有两个成员变量name和age.在外部对类的成员变量的操作,主要包括取值和赋值.简单的取值操作是x=object.var,简单的赋值操作是object.var=value. >>> actress = Actress() >>> actress.name #取值操作 'TianXin' >&g

  • Python中装饰器高级用法详解

    在Python中,装饰器一般用来修饰函数,实现公共功能,达到代码复用的目的.在函数定义前加上@xxxx,然后函数就注入了某些行为,很神奇!然而,这只是语法糖而已. 场景 假设,有一些工作函数,用来对数据做不同的处理: def work_bar(data): pass def work_foo(data): pass 我们想在函数调用前/后输出日志,怎么办? 傻瓜解法 logging.info('begin call work_bar') work_bar(1) logging.info('cal

随机推荐