装饰器(Decorators)可用来装饰类,属性,及方法,甚至是函数的参数,以改变和控制这些对象的表现,获得一些功能。

装饰器以 @expression 形式呈现在被装饰对象的前面或者上方,其中 expression 为一个函数,根据其所装饰的对象的不同,得到的入参也不同。

以下两种风格均是合法的:

@f @g x
@f
@g
x

ES 中装饰器处于 Stage 2 阶段 ,TypeScript 中通过开启相应编译开关来使用。

{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}

一个简单的示例

一个简单的示例,展示了 TypeScript 中如何编写和使用装饰器。

function log(
_target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = function() {
console.log(`method ${propertyKey} called`);
return originalMethod.apply(this, arguments);
};
} class Test {

@log

static sayHello() {

console.log("hello");

}

} Test.sayHello();

上面的示例中,创建了名为 log 的方法,它将作为装饰器作用于类的方法上,在方法被调用时输出一条日志。作为装饰器的 log 函数其入参在后面会介绍。

执行结果:

method sayHello called
hello

装饰器的工厂方法

上面的装饰器比较呆板,设想我们想将它变得更加灵活和易于复用一些,则可以通过创建一个工厂方法来实现。因为本质上装饰器就是个普通函数,函数可通过另外的函数来创建和返回,同时装饰器的使用本质上也是一个函数调用。通过传递给工厂方法不同的参数,以获得不同表现的装饰器。

function logFactory(prefix: string) {
return function log(
_target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = function() {
console.log(`method ${propertyKey} called`);
return originalMethod.apply(this, arguments);
};
};
} class Test {

@logFactory("[debug]")

static sayHello() {

console.log("hello");

}

@logFactory("[info]")

static sum() {

return 1 + 1;

}

} Test.sayHello();

Test.sum();

执行结果:

[debug] method sayHello called
hello
[info] method sum called

多个装饰器

多个装饰器可同时作用于同一对象,按顺序书写出需要运用的装饰器即可。其求值(evaluate)和真正被执行(call)的顺序是反向的。即,排在前面的先求值,排在最后的先执行。

譬如,

function f() {
console.log("f(): evaluated");
return function(target, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("f(): called");
};
} function g() {

console.log("g(): evaluated");

return function(target, propertyKey: string, descriptor: PropertyDescriptor) {

console.log("g(): called");

};

} class C {

@f()

@g()

method() {}

}

求值 的过程就体现在装饰器可能并不直接是一个可调用的函数,而是一个工厂方法或其他表达式,只有在这个工厂方法或表达式被求值后,才得到真正被调用的装饰器。

所以在这个示例中,先依次对 f() g() 求值,再从 g() 开始执行到 f()

运行结果:

f(): evaluated
g(): evaluated
g(): called
f(): called

不同类型的装饰器

类的装饰器

作用于类(Class)上的装饰器,用于修改类的一些属性。如果装饰器有返回值,该返回值将替换掉该类的声明而作为新的构造器使用。

装饰器入参:

  • 类的构造器。

示例:

function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
} @sealed

class Greeter {

greeting: string;

constructor(message: string) {

this.greeting = message;

}

greet() {

return "Hello, " + this.greeting;

}

}

@sealed 将类进行密封,将无法再向类添加属性,同时类上属性也变成不可配置的(non-configurable)。

另一个示例:

function classDecorator<T extends { new (...args: any[]): {} }>(
constructor: T
) {
return class extends constructor {
newProperty = "new property";
hello = "override";
};
} @classDecorator

class Greeter {

property = "property";

hello: string;

constructor(m: string) {

this.hello = m;

}

} console.log(new Greeter("world"));

因为 @classDecorator 中有返回值,这个值将替换本来类的定义,当 new 的时候,使用的是装饰器中返回的构造器来创建类。

方法的装饰器

装饰器作用于类的方法时可用于观察,修改或替换该方法。如果装饰器有返回值,将替换掉被作用方法的属性描述器(roperty Descriptor)。

装饰器入参依次为:

  • 作用于静态方法时为类的构造器,实例方法时为类的原型(prototype)。
  • 被作用的方法的名称。
  • 被作用对象的属性描述器。

示例:

function enumerable(value: boolean) {
return function(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.enumerable = value;
};
} class Greeter {

greeting: string;

constructor(message: string) {

this.greeting = message;

} @enumerable(false)

greet() {

return "Hello, " + this.greeting;

}

}

上面示例中 @enumerable 改变了被装饰方法的 enumerable 属性,控制其是否可枚举。

类的方法可以是设置器(setter)或获取器(getter)。当两者成对出现时,应当只对其中一个运用装饰器,谁先出现就用在谁身上。因为装饰器应用时是用在 getset 两者合并的属性描述器上的。

class Test {
private _foo = 1;
@logFactory("[info]")
get foo() {
return this._foo;
}
//

TypeScript 装饰器的更多相关文章

  1. 从C#到TypeScript - 装饰器

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  2. 基于TypeScript装饰器定义Express RESTful 服务

    前言 本文主要讲解如何使用TypeScript装饰器定义Express路由.文中出现的代码经过简化不能直接运行,完整代码的请戳:https://github.com/WinfredWang/expre ...

  3. Angular 个人深究(一)【Angular中的Typescript 装饰器】

    Angular 个人深究[Angular中的Typescript 装饰器] 最近进入一个新的前端项目,为了能够更好地了解Angular框架,想到要研究底层代码. 注:本人前端小白一枚,文章旨在记录自己 ...

  4. TypeScript装饰器(decorators)

    装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上,可以修改类的行为. 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被 ...

  5. TypeScript 装饰器的执行原理

    装饰器本质上提供了对被装饰对象 Property​ Descriptor 的操作,在运行时被调用. 因为对于同一对象来说,可同时运用多个装饰器,然后装饰器中又可对被装饰对象进行任意的修改甚至是替换掉实 ...

  6. typescript装饰器 方法装饰器 方法参数装饰器 装饰器的执行顺序

    /* 装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为. 通俗的讲装饰器就是一个方法,可以注入到类.方法.属性参数上来扩展类.属性.方法.参数的功能. 常 ...

  7. typescript装饰器定义 类装饰器 属性装饰器 装饰器工厂

    /* 装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为. 通俗的讲装饰器就是一个方法,可以注入到类.方法.属性参数上来扩展类.属性.方法.参数的功能. 常 ...

  8. TypeScript 中装饰器的理解?应用场景?

    一.是什么 装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上 是一种在不改变原类和使用继承的情况下,动态地扩展对象功能 同样的,本质也不是什么高大上的结构,就是一个普通的 ...

  9. Typescript中的装饰器原理

    Typescript中的装饰器原理 1.小原理 因为react中的高阶组件本质上是个高阶函数的调用, 所以高阶组件的使用,我们既可以使用函数式方法调用,也可以使用装饰器. 也就是说,装饰器的本质就是一 ...

随机推荐

  1. 【转】ruby 时间日期处理

    我们可以使用Time类来生成一个当前时间的对象: t = Time.new 或 t = Time.now Time类有类方法mktime(同义方法是local方法)来根据传入的参数生成时间对象,并且它 ...

  2. PCB设计基础及技巧

    一.设计步骤 (1)PCB布局 先放置接口类外设: 根据飞线的接口方向,定位各个模块的方位: 局部模块化(按照一个方向逐个局部化): (2)PCB布线 设置设计规则: 先布过孔(电源.地.长线),防止 ...

  3. J2EE 工作中注意事项

    [转载于http://www.cnblogs.com/hemingwang0902/archive/2012/01/06/2314215.html] 根据当前项目中代码存在的一些问题,编写了一个编码注 ...

  4. 使用union来遍历结构体中的成员

    前几天和实验室的同学讨论问题的时候发现他使用的一段数据校验的代码自己以前没有接触过,今天有空就把它整理了一下. #include <stdio.h> #include <stdlib ...

  5. 从Github远程库安装Node.JS

    3)从Github远程库安装Node.JS在这个方法中我们需要一些步骤来把Node.js的从Github上的远程的仓库克隆到本地仓库目录 在开始克隆(克隆)包到本地并且配制之前,我们要先安装以下依赖包 ...

  6. 修改TABLE中的Column的属性

    删除主键名 这个主键名并不是列名,而是主键约束的名字,如果当时设置主键约束时没有制定约束的名字 设置主键的语句:ALTER TABLE P add constraint pk PRIMARY KEY ...

  7. rest framework 权限

    一.权限示例 需求:不同的用户类型有不同的权限 普通用户:只能查看个人信息相关,序号:1 VIP 用户:只能查看个人信息相关,序号:2 SVIP 用户:查看订单相关信息,序号:3 1.新建 app/u ...

  8. 【linux-./configure 配置文件时出错问题】

    环境是:centos 5.6 安装系统时,可能安装的是标准的精简版本,所以没有选择软件依赖包,很多软件都没有安装. 现在需要安装软件,安装软件时报错: make: *** 没有指明目标并且找不到 ma ...

  9. C# Object reference not set to an instance of an object.

    一.问题 Object reference not set to an instance of an object. (你调用的对象是空的) 二.解决问题 在使用 c# 的查询时,先筛选后在关联其他表 ...

  10. html5表单及新增的改良元素

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...