获取类构造函数参数名称

set*_*tec 5 javascript reflection dependency-injection ecmascript-6

我正在为 ES 类实现某种依赖注入解决方案,为此我需要知道类构造函数参数的确切名称。

当我获得类或类静态方法的字符串形式时,它确实提供了完整的代码(通常用于函数),但对于类构造函数则没有。

class C { constructor(a, b) { }; static m(x,y) { } }
console.log(C);
console.log(C.constructor);
console.log(C.m);
Run Code Online (Sandbox Code Playgroud)

结果是

class C { constructor(a, b) { }; static m(x,y) { } }
ƒ Function() { [native code] }
ƒ m(x,y) { }
Run Code Online (Sandbox Code Playgroud)

结果,我必须解析整个类代码以提取构造函数参数部分,使用这样的正则表达式

C.toString().split(/constructor\s*[^\(]*\(\s*([^\)]*)\)/m)
Run Code Online (Sandbox Code Playgroud)

有没有更简洁的方法来获取构造函数参数名称?

更新:感谢您的所有意见和评论,但我非常清楚缩小的工作原理、TS 装饰器的工作原理以及 Angular/AngularJS DI 的实现方式及其工作原理。它与问题无关。问题是:

是否可以像函数一样获得构造函数代码?

Twi*_*her 3

参数名称不应被视为识别要注入到 constructor . 正如您所注意到的,JavaScript 的设计目的并不是让您以正确的方式检索这些名称,如果您处于浏览器上下文中,您可能会在发布代码之前缩小代码,从而导致名称丢失。

JavaScript 中的依赖注入机制通常依赖于元数据和自定义构建过程,例如 Angular 的类元数据编译器会内省源代码以生成运行时代码。

话虽这么说,如果您使用 TypeScript,这里是如何通过将元数据附加到类本身来使用参数装饰器实现依赖项注入的最小示例:

const metadataKey = Symbol();

interface InjectableClass {
    new (...args: any[]): any;
    [metadataKey]: any[];
}

function Inject(value: any) {
    return function (target: InjectableClass, key: PropertyKey, paramIndex: number) {
        target[metadataKey] = Object.assign(target[metadataKey] || [], { [paramIndex]: value });
    }
}

class Test {
    static [metadataKey]: any[];
    greetings: string;
    name: string;

    constructor(@Inject('Hello there!') greetings: string, @Inject('Guerric') name: string) {
        this.greetings = greetings;
        this.name = name;
    }

    sayHello() {
        console.log(`${this.greetings} My name is ${this.name}`);
    }
}

function factory<T extends InjectableClass>(clazz: T): InstanceType<T> {
    return new clazz(...clazz[metadataKey]);
}

factory(Test).sayHello();
Run Code Online (Sandbox Code Playgroud)

生成以下 JavaScript:

const metadataKey = Symbol();

interface InjectableClass {
    new (...args: any[]): any;
    [metadataKey]: any[];
}

function Inject(value: any) {
    return function (target: InjectableClass, key: PropertyKey, paramIndex: number) {
        target[metadataKey] = Object.assign(target[metadataKey] || [], { [paramIndex]: value });
    }
}

class Test {
    static [metadataKey]: any[];
    greetings: string;
    name: string;

    constructor(@Inject('Hello there!') greetings: string, @Inject('Guerric') name: string) {
        this.greetings = greetings;
        this.name = name;
    }

    sayHello() {
        console.log(`${this.greetings} My name is ${this.name}`);
    }
}

function factory<T extends InjectableClass>(clazz: T): InstanceType<T> {
    return new clazz(...clazz[metadataKey]);
}

factory(Test).sayHello();
Run Code Online (Sandbox Code Playgroud)

TypeScript 游乐场

使用专用的变体Map来存储元数据而不是将它们附加到类:

const metadataMap = new Map();

interface Constructable {
    new (...args: any[]): any;
}

function Inject(value: any) {
    return function (target: Constructable, key: PropertyKey, paramIndex: number) {
        metadataMap.set(target, Object.assign(metadataMap.get(target) || [], { [paramIndex]: value }));
    }
}

class Test {
    greetings: string;
    name: string;

    constructor(@Inject('Hello there!') greetings: string, @Inject('Guerric') name: string) {
        this.greetings = greetings;
        this.name = name;
    }

    sayHello() {
        console.log(`${this.greetings} My name is ${this.name}`);
    }
}

function factory<T extends Constructable>(clazz: T): InstanceType<T> {
    return new clazz(...metadataMap.get(clazz));
}

factory(Test).sayHello();
Run Code Online (Sandbox Code Playgroud)

TypeScript 游乐场

  • 抱歉,但我知道这一切,而 angularjs (1.*) 中的 DI 是基于使用正则表达式解析函数代码,没有打字稿。虽然我仍然感谢您提出意见,但它并没有回答问题,而是指出没有明确的方法来获取构造函数的参数名称或代码。 (3认同)
  • 答案是:不,没有更干净的方法,自从 AngularJS 以来,这方面没有任何真正的改变 (2认同)