日期:2022年7月23日标签:JavaScript

JavaScript Decorator 原理 #

JavaScript Decorator 的使用方法可以看这两篇文章:Legacy JavaScript DecoratorJavaScript Decorator

本章内容介绍 Decorator 内部的原理,typescript 是如何实现 decorator 的。

tsconfig #

为了启用 decorator 语法,需要配置 tsconfig.json 文件:

{
    "compilerOptions": {
        "target": "ESNext",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": false
    },
    "include": ["./**/*.ts"]
}

编译 decorator #

包含 decorator 语法的 TypeScript 文件:

function Enumerable(
    target: any,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor,
) {
    descriptor.enumerable = true;
    return descriptor;
}

class Demo {
    @Enumerable
    public foo() {
        // do nothing
    }
}

const demo = new Demo();

编译后的 js 文件:

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 Enumerable(target, propertyKey, descriptor) {
    descriptor.enumerable = true;
    return descriptor;
}
class Demo {
    foo() {
        // do nothing
    }
}
__decorate([
    Enumerable
], Demo.prototype, "foo", null);
const demo = new Demo();

可以看到编译后的 js 文件中有一个 __decorate 函数,__decorate 函数就是 decorator 的原理。

格式化 __decorate #

为了看清楚 __decorate,将其结构展开,并分析:

var __decorate = (
    // 如果 this.__decorate 已经定义,则直接取 this.__decorate
    // 这里应该是为以后兼容,如果以后 javascript 已经包含了 __decorate 定义,这里就不需要更改了
    (this && this.__decorate)
    ||
    // 如果 this.__decorate 没有定义,则返回下面的函数
    // 函数后面三个参数的意义分别是:
    // target: 类对象
    // key: 方法名称
    // desc: 方法的 descriptor
    function(decorators, target, key, desc) {
        var c = arguments.length,
        // 参数个数小于 3,则 r 为 target,
        // 参数个数大于3,判断 desc 是否为 null,如果为 null 则将 desc 重新赋值为 descriptor,并将 r 赋值为 descriptor
            r = (
                c < 3
                    ? target
                    : (
                        desc === null
                            ? desc = Object.getOwnPropertyDescriptor(target, key)
                            : desc
                    )
            ),
            d;
        // 判断 Reflect.decorate 函数是否存在,如果存在,则直接调用 Reflect.decorate 函数
        // 实际上 Reflect.decorate 函数还没有实现,所以这里也是为了以后做准备,说不定哪一天 javascript 标准就实现了 Reflect.decorate 函数
        if (
            typeof Reflect === "object"
            &&
            typeof Reflect.decorate === "function"
        ) {
            r = Reflect.decorate(decorators, target, key, desc);
        } else {
            // 根据参数的个数(c),依次执行 decorators 函数
            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
        );
    }
);

其他类型的 decorator 编译后的文件 #

parameter decorator #

ts 源文件:

function LogParameter(
    target: any,
    propertyKey: string | symbol,
    parameterIndex: number,
) {
    console.log(target);
    console.log(propertyKey);
    console.log(parameterIndex);
}

class ParameterExample {
    public logThis(
        first: string = "",
        @LogParameter greeting: string = "Hello, world",
    ) {
        // do nothing
    }
}

const demoParameter = new ParameterExample();

编译后的 js 文件:

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 __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
function LogParameter(target, propertyKey, parameterIndex) {
    console.log(target);
    console.log(propertyKey);
    console.log(parameterIndex);
}
class ParameterExample {
    logThis(first = "", greeting = "Hello, world") {
        // do nothing
    }
}
__decorate([
    __param(1, LogParameter)
], ParameterExample.prototype, "logThis", null);
const demoParameter = new ParameterExample();

property decorator #

ts 源文件:

function LogProperty(
    target: any,
    propertyKey: string | symbol,
) {
    console.log(target);
    console.log(propertyKey);
}

class PropertyExample {
    @LogProperty
    public greeting: string;

    constructor() {
        this.greeting = "Hello, world";
    }
}

const demoExample = new PropertyExample();

编译后的 js 文件:

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 LogProperty(target, propertyKey) {
    console.log(target);
    console.log(propertyKey);
}
class PropertyExample {
    constructor() {
        this.greeting = "Hello, world";
    }
}
__decorate([
    LogProperty
], PropertyExample.prototype, "greeting", void 0);
const demoExample = new PropertyExample();

class decorator #

ts 源文件:

function LogClass(target: any) {
    console.log(target.constructor.name);
}

@LogClass
class ClassExample {
    // do nothing
}

const demoClass = new ClassExample();

编译后的 js 文件:

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 LogClass(target) {
    console.log(target.constructor.name);
}
let ClassExample = class ClassExample {
};
ClassExample = __decorate([
    LogClass
], ClassExample);
const demoClass = new ClassExample();

(完)

目录