TypeScript装饰器实现原理
费尔明娜,我等待这个机会,已经有51年9个月零4天了,在这段时间里,我一直爱着你,从我第一眼见到你,直到现在,我第一次向你表达我的誓言,我永远爱你,忠贞不渝。———《霍乱时期的爱情》
在学习设计模式
的时候看到了一种装饰器模式
,随后脑子就联想到了TypeScript
中的装饰器,然后通过TypeScript
官网提供的在线IDEA工具
,我终于懂了TypeScript
装饰器的实现原理。
先来看一看在JavaScript
中装饰器是如何实现的。
function fun(){
console.log(1);Ï
}
我想给fun
函数在不改变函数本身的情况下扩展一些功能,如果是TypeScript
的话就会很容易实现,因为TypeScript
中有装饰器
,那我们在JavaScript
中如何实现呢?。
这里我使用原型链
的方式实现一下。
// 前置的before方法
Function.prototype.before = function (fn) {
var _this = this;
return function () {
fn.apply(this, arguments); // 前置callback调用
return _this.apply(this, arguments); // 返回自身调用
}
// 后置的after方法
Function.prototype.after = function (fn) {
var _this = this;
return function () {
var ret = _this.apply(this, arguments); // 自身调用
fn.apply(this, arguments); // 后置callback调用
return ret;
}
}
首先看一下before
和after
函数为什么要返回一个函数呢?这是为了在调用的时候实现链式调用,了解jQuery
的话应该就知道jQuer
就是使用返回函数的方式来实现方法的链式调用。
接下来我们来使用这个装饰器
var resultFn = fun.before(() => {
console.log(0)
}).after(() => {
console.log(2);
})
resultFn(); // 0 1 2
通过原型链
的方式我们实现了不改变函数本身的情况下进行扩展。
那这样的话TypeScript
中的装饰器是否也是这样实现的呢?我带着疑问去看了TypeScript
装饰器生成的JavaScript
代码。
我们都知道TypeScript
装饰器有很多种,我们先来看一下类装饰器。
function RT(target: any) {}
@RT
class Demo {}
上面的TypeScript
代码会编译成下面的JavaScript
代码
"use strict";
var __decorate = (this && this.__decorate) ||
function (decorators, target, key, desc) {
var c = arguments.length,
r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if (d = decorators[i])
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
function RT(target) {
}
let Demo = class Demo {
};
Demo = __decorate([
RT
], Demo);
以上代码中最主要的部分就是__decorate
函数的实现,那么我们一起来看看__decorate
是怎么实现的。
__decorate
函数接受了三个参数,分别是:
- decorators 所有装饰器函数
- target 目标类
- key 属性
- desc 属性的描述
参数个数小于3
的话,就是类装饰器
,否则就是方法装饰器
、属性装饰器
和参数装饰器
。
那么这个变量r
就赋值了Demo
类
这里的Reflect.decorate
是期望以后JavaScript
中会支持Reflect.decorate
方法。
遍历所有的装饰器函数,调用装饰器函数,如果是类装饰器
的话,将r
传递并返回。
这样就实现了对类的扩展,其实还是非常简单的,接下来我们来看一下方法装饰器的实现。
function RT(target: any, key: string) { }
class Demo {
@RT
sayHello() {
console.log('sayHello methods');
}
}
上面的TypeScript
代码会编译成下面的JavaScript
代码
"use strict";
var __decorate = (this && this.__decorate) ||
function (decorators, target, key, desc) {
var c = arguments.length,
r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if (d = decorators[i])
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
function RT(target, key) { }
class Demo {
sayHello() {
console.log('sayHello methods');
}
}
__decorate([
RT
], Demo.prototype, "sayHello", null);
__decorate
方法实现是相同的,不同地方就是参数调用__decorate
方法传入的参数个数。
参数个数大于等于3
,使用getOwnPropertyDescriptor
方法获取属性的描述赋值给desc
变量和r
变量
遍历所有的装饰器函数,调用装饰器函数,将需要的参数传递,并使用defineProperty
定义一个key
赋值给target
,并返回r
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r
这个表达式我会以为r
的值会是d
函数的调用,但是我错了,函数没有返回值的话会返回undefined
,那么就是undefined || r
,所以r
的值还是r
c > 3 && r && Object.defineProperty(target, key, r), r;
这个表达式我也会很懵,调试了一下,结果是这样的
1 && 2 && 3 && 4, 5 // 返回5
1 && false && false, 5 // 返回5
其实逗号之前的表达式我们都可以认为是执行函数的作用或者是赋值的作用,它不起到返回的作用。
转载自:https://juejin.cn/post/7352075719151681574