📜 装饰器
@ 还未通过 ES 提案,现阶段,建议仅在 TypeScript 中使用。
装饰模式
装饰模式 是 JavaScript 中一个十分普通的设计 , 它的本质并不复杂。
假如有一个函数:
function slow(x) {
return x;
}
1
2
3
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
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
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
2
3
4
5
6
7
8
9
10
11
运行结果:
$ deno run a.ts
22222 [Function: Hello]
11111 [Function: Hello]
1
2
3
2
3
装饰器会立刻执行,而不是调用的时候才执行。
那如何实现 当函数调用的时候, 继续“增强”?
记得函数是“一等公民”吗?
详细案例详见下文。
带参数 的 装饰器
@装饰器("/hello", "world")
class Hello {}
1
2
2
装饰器需要一些修改
function 装饰器(p1: string, p2: string) {
return function(target) {
// 真正装饰器
};
}
1
2
3
4
5
2
3
4
5
装饰器类型
装饰器分为多类,每一种类型的 参数 都不一样。
- 类装饰器
- 访问器装饰器
- 属性装饰器
- 方法装饰器
- 参数装饰器
没有函数装饰器!没有函数装饰器!没有函数装饰器!!!
类装饰器
function 装饰器(target) {}
@装饰器(18)
class Hello {}
1
2
3
4
2
3
4
target 是 Hello.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
2
3
4
5
6
7
8
9
10
11
12
propertyKey 是 方法名
descriptor 是 Object.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
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
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
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
2
3
4
5
6
7