📜 装饰器

@ 还未通过 ES 提案,现阶段,建议仅在 TypeScript 中使用。

装饰模式

装饰模式 是 JavaScript 中一个十分普通的设计 , 它的本质并不复杂。

假如有一个函数:

function slow(x) {
  return x;
}
1
2
3

现在我希望他翻倍

function 翻倍(func) {
  return (...args) => {
    // func 前
    console.log("这里也可以处理一些逻辑");

    const res = func(...agrs);

    // func 后
    return res * 2;
  };
}

// 现在 slow2 就可以翻倍了
const slow2 = 翻倍(slow2);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

接受一个func(函数)作为参数,并且在func执行的 做一些处理,最终达到增强函数的作用,这就是 装饰模式。

装饰模式 最强之处在于,它不会影响原本逻辑,只是单纯的增强。

装饰器`@`

装饰器符号 @,最初应该起源于 Python


def decorator(f):
    print "my decorator"
    return f

@decorator
def myfunc():
    print "my function"

myfunc()

# 最总会输出:
# my decorator
# my function





 








1
2
3
4
5
6
7
8
9
10
11
12
13
14

TypeScript 的 装饰器

装饰器 本质 就是一个函数

function 装饰器1(target: any) {
  console.log("11111", target);
}

function 装饰器2(target: any) {
  console.log("22222", target);
}

@装饰器1
@装饰器2
class Hello {}
1
2
3
4
5
6
7
8
9
10
11

运行结果:

$ deno run a.ts
22222 [Function: Hello]
11111 [Function: Hello]
1
2
3

装饰器会立刻执行,而不是调用的时候才执行。

那如何实现 当函数调用的时候, 继续“增强”?

记得函数是“一等公民”吗?

详细案例详见下文。

带参数 的 装饰器

@装饰器("/hello", "world")
class Hello {}
1
2

装饰器需要一些修改

function 装饰器(p1: string, p2: string) {
  return function(target) {
    // 真正装饰器
  };
}
1
2
3
4
5

装饰器类型

装饰器分为多类,每一种类型的 参数 都不一样。

  • 类装饰器
  • 访问器装饰器
  • 属性装饰器
  • 方法装饰器
  • 参数装饰器

没有函数装饰器!没有函数装饰器!没有函数装饰器!!!

类装饰器

function 装饰器(target) {}

@装饰器(18)
class Hello {}


 

1
2
3
4

targetHello.prototype,是原型!

方法装饰器

function 装饰器(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {}

class Hello {
  @装饰器
  say() {
    console.log("hello");
  }
}







 
 
 
 

1
2
3
4
5
6
7
8
9
10
11
12

propertyKey 是 方法名

descriptorObject.getOwnPropertyDescriptorsopen in new window

其中 descriptor.value 就是原方法。

方法属性装饰器

function 装饰器(target: any, name: string, index: number) {}

class Hello {
  say(@装饰器 what: string) {
    // ...
  }
}



 



1
2
3
4
5
6
7

target 静态类是构造函数,正常情况是原型,如:Hello.prototype

name 是 方法名,如:say

index 是 参数的位置,如:0

属性装饰器

function 装饰器(target: any, name: string) {}

class Hello {
  @装饰器
  private name: string = "张三";

  say() {
    l("hello");
  }
}



 






1
2
3
4
5
6
7
8
9
10

target 依旧是原型,静态方法除外

name 则是 obj[key] 的 key

访问器装饰器

get 和 set ,很少用。

先省略,详见 官方文档open in new window

🌰

最后,上一个简单都栗子

function 装饰器(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {
  // 记录原方法
  const method = descriptor.value;

  // 编写新方法
  // 最好使用 function ,而不是箭头函数。因为箭头函数没有this,而 普通函数有。
  descriptor.value = function(...a: any[]) {
    console.log("-- 某年某月某天-- ");
    method.apply(this, a);
    console.log(`我叫 ${(this as any).name},你叫啥?`);
  };
}

class Hello {
  name: string = "张三";

  @装饰器
  say() {
    console.log("hello");
  }
}









 







 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

然后使用

new Hello().say();

// 输入如下内容:

// -- 某年某月某天--
// hello
// 我叫 张三,你叫啥?
1
2
3
4
5
6
7

参考文献