总目录

从C#到TypeScript - 装饰器

在C#里面如果想要不直接修改类或方法,但给类或方法添加一些额外的信息或功能,可以想到用Attribute,这是一个十分方便的功能装饰器。

用TypeScript同样也可以利用装饰器来给类、函数、属性以及参数添加附加功能,装饰器是ES7的一个提案,在TypeScript里已经有实现可用,不过需要在tsconfig.json里启用experimentalDecorators

  1. "compilerOptions": {
  2. ..., // other options
  3. "experimentalDecorators": true
  4. }

装饰器介绍

TypeScript中装饰器可以应用到类、方法、属性及函数参数上,而且可以同时应用多个。

装饰器的写法是@name()()可以不要,也可以在里面写一些参数。

  1. @Testable
  2. @Log('controller')
  3. class Controller{
  4. @GET
  5. getContent(@QueryParam arg: string): string{
  6. return '';
  7. }
  8. }

装饰器的实现

装饰器根据实现可以分两种:

一种是不带括号,和属性一样,如@Testable

  1. function Testable(target: Function) { // 类、方法、属性、方法参数的参数各不相同
  2. //这里可以记录一些信息到target,或者针对target做一些处理,如seal
  3. }

另外一种是带括号的,和函数一样,如@Log('controller'),实现函数里的参数就是括号里的参数,而且需要返回一个function

  1. function Log(name: string) { // name就是传进来的参数'controller'
  2. return function(target: Function) { // 类、方法、属性、方法参数的参数各不相同
  3. // 这里可以根据name和target来做一些处理
  4. }
  5. }

类装饰器

上面的(target: Function)其实就是类的装饰器参数,指向的是类的构造函数,如果想给类加一个简单的seal功能,可以这样做:

  1. function sealed(target: Function) {
  2. Object.seal(target);
  3. Object.seal(target.prototype);
  4. }
  5. @sealed
  6. class Test{
  7. }
  8. Test.prototype.test = ''; // 运行时出错,不能添加

上面的sealed就是类的装饰器,target指构造函数,类装饰器就这么一个参数。

方法装饰器

方法装饰器的使用方法和类装饰器类似,只是参数不一样,方法装饰器有三个参数:

  1. 如果装饰的是静态方法,则是类的构造函数,如果是实例方法则是类的原型。
  2. 方法的名字。
  3. 方法的PropertyDescriptor

    PropertyDescriptor即属性描述符,有

    configurable 是否可以配置,如动态添加删除函数属性之类

    writable 是否可写,可以用来设置只读属性

    enumerable 是否可枚举,即是否能在for...in中能枚举到

    value 对象或属性的值

有了这些参数就可以很好的给方法添加一些功能,比如下面实现类型WebApi里的Get的路由:

  1. const Router = Symbol(); // 唯一key,用来存装饰器的信息
  2. function GET(path?: string) { // GET带了个可选参数
  3. return (target: any, name: string) => setMethodDecorator(target, name, 'GET', path);
  4. }
  5. //把method和path存起来,路由查找的时候就可以用了
  6. function setMethodDecorator(target: any, name: string, method: string, path?: string){
  7. target[Router] = target[Router] || {};
  8. target[Router][name] = target[Router][name] || {};
  9. target[Router][name].method = method;
  10. target[Router][name].path = path;
  11. }
  12. // 通过PropertyDescriptor来设置enumerable
  13. function Enumerable(enumerable: boolean) {
  14. return (target: any, name: string, descriptor: PropertyDescriptor) => {
  15. descriptor.enumerable = enumerable;
  16. };
  17. }
  18. class Controller{
  19. @GET
  20. @Enumerable(true)
  21. getContent(arg: string): string{
  22. return '';
  23. }
  24. }

参数装饰器

方法参数同样可以有装饰器,同样有三个参数,前两个参数和方法的一致,最后一个参数是所装饰的参数的位置。

能过参数装饰器可以给方法动态的检查或设置参数值,下面是检查参数是否为空,为空则抛出异常。

  1. const CheckNullKey = Symbol();
  2. const Router = Symbol();
  3. // 把CheckNull装饰的参数存起来
  4. function CheckNull(target: any, name: string, index: number) {
  5. target[Router] = target[Router] || {};
  6. target[Router][name] = target[Router][name] || {};
  7. target[Router][name].params = target[Router][name].params || [];
  8. target[Router][name].params[index] = CheckNullKey;
  9. }
  10. // 找出CheckNull的参数,并检查参数值,为空则抛异常,否则继续执行方法
  11. function Check(target: any, name: string, descriptor: PropertyDescriptor) {
  12. let method = descriptor.value;
  13. descriptor.value = function () {
  14. let params = target[Router][name].params;
  15. if (params) {
  16. for (let index = 0; index < params.length; index++) {
  17. if (params[index] == CheckNullKey && // 找到CheckNull的参数并抛异常
  18. (arguments[index] === undefined || arguments[index] === null)) {
  19. throw new Error("Missing required argument.");
  20. }
  21. }
  22. }
  23. return method.apply(this, arguments);
  24. }
  25. }
  26. class Controller{
  27. @Check
  28. getContent(@CheckNull id: string): string{
  29. console.info(id);
  30. return id;
  31. }
  32. }
  33. new Controller().getContent(null); // error : Missing required argument.

属性装饰器

用法同上,参数只有两个,和类装饰器的前两个一样,常用来标识属性的特性。

  1. function Column(target: any, name: string) {
  2. //把name存起来,这个column仅仅是标识出来对应数据库中的列,常用在ORM框架中
  3. }
  4. class Table{
  5. @Column
  6. name: string;
  7. }

另外还有属性访问器的装饰器,和方法基本一样,同样的三个参数,不过同个属性的getset只能有一个有,而且必须是先声明的那个。

  1. class User {
  2. private _name: string;
  3. @Enumerable(true)
  4. get name(){
  5. return this._name;
  6. }
  7. set name(value: string) {
  8. this._name = value;
  9. }
  10. }

多个装饰器的执行顺序

一个声明可以添加多个装饰器,所以会有个执行先后顺序。

首先从上到下执行装饰器函数,然后再从下往上应用带括号的装饰器返回的函数。

  1. function Test1(){
  2. console.info('eval test1');
  3. return function(target: any, name: string, descriptor: PropertyDescriptor){
  4. console.info('apply test1');
  5. }
  6. }
  7. function Test2(){
  8. console.info('eval test2');
  9. return function(target: any, name: string, descriptor: PropertyDescriptor){
  10. console.info('apply test2');
  11. }
  12. }
  13. class User1{
  14. @test1()
  15. @Test2()
  16. getName(){
  17. }
  18. }

结果是:

  1. eval test1
  2. eval test2
  3. apply test2
  4. apply test1

总之,装饰器等于引入了天然的装饰模式,给类,方法等添加额外功能。不过装饰器目前还不算太稳定,但是由于确实方便,已经有成熟项目在使用了。

从C#到TypeScript - 装饰器的更多相关文章

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

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

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

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

  3. TypeScript装饰器(decorators)

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

  4. TypeScript 装饰器

    装饰器(Decorators)可用来装饰类,属性,及方法,甚至是函数的参数,以改变和控制这些对象的表现,获得一些功能. 装饰器以 @expression 形式呈现在被装饰对象的前面或者上方,其中 ex ...

  5. TypeScript 装饰器的执行原理

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

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

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

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

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

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

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

  9. Typescript中的装饰器原理

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

随机推荐

  1. 100套新鲜免费的PS笔刷下载

    这篇文章所有的Photoshop笔刷都是免费且高质量的.笔刷总类齐全:有飞鸟.冰块.水.树枝.喷墨.科技元素.皮肤纹理.烟火等等!用它们来加速你的工作流程,提升作品档次吧!”一挪妖娆举动,一刷风情万种 ...

  2. pip安装icu失败:Command "python setup.py egg_info" failed with error code 1 in

    问题 Mac 下通过 pip 安装 icu 失败. 解决办法及原因 问题的原因是因为icu库中的某一行代码找不到一个文件,获取不到ICU_VERSION的值. # Install icu brew i ...

  3. 2)Java学习笔记:匿名内部类

    为什么要使用匿名内部类 ①如果以前的类有一些缺陷,只是想在某一个模块进行修复,可以在引用该类的地方使用匿名内部类,在overRide方法进行修复. ②如果一个类,需要派生出很多类,而且这些类大多只是在 ...

  4. pyv8安装

    http://www.thinksaas.cn/topics/0/400/400915.html

  5. c++初学(电梯实验)

    模拟电梯载人实验 Elevator.h class Elevator{public:    Elevator();    ~Elevator();    void getNowNum();       ...

  6. Python3基础 map 与 lambda表达式配合 将指定系列元素乘2

    镇场诗: 诚听如来语,顿舍世间名与利.愿做地藏徒,广演是经阎浮提. 愿尽吾所学,成就一良心博客.愿诸后来人,重现智慧清净体.-------------------------------------- ...

  7. UVa 10670 - Work Reduction

    题目大意:对n份文件进行处理使其减少到m份,有l个机构可供选择.每个机构提供两种方案:每减少一份收费a元,或者减少到文件数量的一半收费b元.根据各个机构收取费用进行排序. 很直接的题目,直接进行模拟就 ...

  8. margin的简单应用

    今晚学了盒模型的marg部分,简单仿下京东的官网首页部分 第一次制作,尽管看来实在惨不忍睹,毕竟娘不嫌儿丑,之后多加努力吧,这几天尽量加快学习进度,能单独制作一张精美的网页最好 附上代码 <!D ...

  9. 在vhd中安装win7,并建立分差vhd

    准备:硬盘分区激活第一个分区; imagex.exe; install.wim; winpe boot pc 1.cmd命令下,创建主vhd      (1)diskpart       (打开dis ...

  10. iOS网络高级编程:iPhone和iPad的企业应用开发(书籍学习)

    作者:Jack Cox.Nathan Jones.John Szumski 译者:张龙  勘误  前言  第 I 部分 理解iOS与企业网络 这一部分从高层次概览了iOS网络以及针对移动网络架构的最佳 ...