TypeScript 装饰器的执行原理
Posted wayou
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TypeScript 装饰器的执行原理相关的知识,希望对你有一定的参考价值。
装饰器本质上提供了对被装饰对象 Property? Descriptor 的操作,在运行时被调用。 因为对于同一对象来说,可同时运用多个装饰器,然后装饰器中又可对被装饰对象进行任意的修改甚至是替换掉实现,直观感觉会有一些主观认知上的错觉,需要通过代码来验证一下。 比如,假若每个装饰器都对被装饰对象的有替换,其结果会怎样? 多个装饰器的应用通过编译运行以下示例代码并查看其结果可以得到一些直观感受: function f()
console.log("f(): evaluated");
return function(_target: any, key: string, descriptor: PropertyDescriptor)
const original = descriptor.value;
descriptor.value = function(...args: any[])
console.log(`[f]before $key called`, args);
const result = original.apply(this, args);
console.log(`[f]after $key called`);
return result;
;
console.log("f(): called");
return descriptor;
;
function g()
console.log("g(): evaluated");
return function(_target: any, key: string, descriptor: PropertyDescriptor)
const original = descriptor.value;
descriptor.value = function(...args: any[])
console.log(`[g]before $key called`, args);
const result = original.apply(this, args);
console.log(`[g]after $key called`);
return result;
;
console.log("g(): called");
return descriptor;
;
class C
@f()
@g()
foo(count: number)
console.log(`foo called $count`);
const c = new C();
c.foo(0);
c.foo(1); 先放出执行结果: f(): evaluated
g(): evaluated
g(): called
f(): called
[f]before foo called [ 0 ]
[g]before foo called [ 0 ]
foo called 0
[g]after foo called [ 0 ]
[f]after foo called [ 0 ]
[f]before foo called [ 1 ]
[g]before foo called [ 1 ]
foo called 1
[g]after foo called [ 1 ]
[f]after foo called [ 1 ] 下面来详细分析。 编译后的装饰器代码首页看看编译后变成 javascript 的代码,毕竟这是实际运行的代码: 编译后的代码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;
;
var __metadata = (this && this.__metadata) || function (k, v)
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
;
function f()
console.log("f(): evaluated");
return function (_target, key, descriptor)
var original = descriptor.value;
descriptor.value = function ()
var args = [];
for (var _i = 0; _i < arguments.length; _i++)
args[_i] = arguments[_i];
console.log("[f]before " + key + " called", args);
var result = original.apply(this, args);
console.log("[f]after " + key + " called", args);
return result;
;
console.log("f(): called");
return descriptor;
;
function g()
console.log("g(): evaluated");
return function (_target, key, descriptor)
var original = descriptor.value;
descriptor.value = function ()
var args = [];
for (var _i = 0; _i < arguments.length; _i++)
args[_i] = arguments[_i];
console.log("[g]before " + key + " called", args);
var result = original.apply(this, args);
console.log("[g]after " + key + " called", args);
return result;
;
console.log("g(): called");
return descriptor;
;
var C = /** @class */ (function ()
function C()
C.prototype.foo = function (count)
console.log("foo called " + count);
;
__decorate([
f(),
g(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Number]),
__metadata("design:returntype", void 0)
], C.prototype, "foo", null);
return C;
());
var c = new C();
c.foo(0);
c.foo(1); 先看经过 TypeScript 编译后的代码,重点看这一部分: var C = /** @class */ (function ()
function C()
C.prototype.foo = function (count)
console.log("foo called " + count);
;
__decorate([
f(),
g(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Number]),
__metadata("design:returntype", void 0)
], C.prototype, "foo", null);
return C;
()); tslib 中装饰器的实现其中 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;
; 装饰器的执行顺序配合编译后代码和这里装饰器的实现来看,进一步之前了解到的关于装饰器被求值和执行的顺序, 源码中应用装饰器的地方: @f()
@g()
foo(count: number)
console.log(`foo called $count`);
然后这里的 __decorate(
[
+ f(),
+ g(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Number]),
__metadata("design:returntype", void 0)
],
C.prototype,
"foo",
null
); 然后在 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;
; 其中 Object.defineProperty(target, key, r) 所以,像示例代码中多个装饰器均对被装饰对象有修改,原则上和多次调用
|
以上是关于TypeScript 装饰器的执行原理的主要内容,如果未能解决你的问题,请参考以下文章