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 中装饰器的实现其中 __decorate为 TypeScript 经 tslib 提供的 Decorator 实现,其源码为: 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}`); } 然后这里的 @f() @g()按照该顺序传递给了 __decorate函数, __decorate( [ + f(), + g(), __metadata("design:type", Function), __metadata("design:paramtypes", [Number]), __metadata("design:returntype", void 0) ], C.prototype, "foo", null ); 然后在 __decorate函数体中,对传入的 decorators从数据最后开始,取出装饰器函数顺次执行, 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; }; 其中 r便是装成器的返回,会被当作被装饰对象的新的属性描述器(Property Descriptor)来重新定义被装饰的对象: Object.defineProperty(target, key, r) 所以,像示例代码中多个装饰器均对被装饰对象有修改,原则上和多次调用 Object.defineProperty()相当。
|
- Typescript中的装饰器原理
- Typescript中的装饰器原理
- python基础:复习整理笔记(一)____关于 工具、程序执行原理、python风格规范
- 学习笔记(一)--php执行原理
- javascript的执行原理
- 容器如何处理JSP——原理及执行流程详解
- Sql Server 编译、重编译与执行计划重用原理
- windows内核原理分析之DPC函数的执行(1)
- servlet的执行原理与生命周期
- C语言可变参数函数执行原理以应用
- selenium执行内部原理
- Update语句执行原理
- gem5中event queue执行原理机制具体分析
- MapReduce 原理及执行过程
- 程序内部执行原理
- 【java关键字-Thread】Thread.Join()的用法,及其实现线程顺序执行的原理
- Python装饰器原理
- Python解释执行原理
- hive 结合执行计划 分析 limit 执行原理
- Cubby的plugin的实现原理以及执行顺序分析