How to pass a string or class to a method to create instance

前端 未结 2 1570
心在旅途
心在旅途 2021-01-21 12:37

I am to use the following method, it works by passing a type to it such as obj.addComponent(MyClass). This works just fine.

I tried to modify the typ

2条回答
  •  悲&欢浪女
    2021-01-21 13:15

    There's no way to get the class using a name in javascript, it doesn't have something similar to the java ClassLoader.
    You can get around that by creating your own mechanism, and there are probably many ways to do so, but here are 3 options.

    (1) Maintain a registry for your component classes:

    const REGISTRY: { [name: string]: ComponentType } = {};
    
    class Component {}
    
    class MyComponent1 extends Component {}
    REGISTRY["MyComponent1"] = MyComponent1;
    
    class MyComponent2 extends Component {}
    REGISTRY["MyComponent2"] = MyComponent2;
    
    type ComponentType = {
        new(): T;
    }
    
    function factory(type: ComponentType | string): T {
        return typeof type === "string" ?
            new REGISTRY[type]() as T:
            new type();
    }
    

    (code in playground)

    If you go with this approach then I suggest to make the REGISTRY an object that holds the collection, that way you can add the ctor only and get the name from that.

    There's a variant for this and that's to use a decorator:

    function register(constructor: typeof Component) {
        REGISTRY[(constructor as any).name] = constructor;
    }
    
    @register
    class MyComponent1 extends Component {}
    
    @register
    class MyComponent2 extends Component {}
    

    (code in playground)

    (2) Wrap the components in a namespace (As @Shilly suggested in a comment):

    namespace components {
        export class Component {}
        export class MyComponent1 extends Component {}
        export class MyComponent2 extends Component {}
    
        export type ComponentType = {
            new(): T;
        }
    
        export function forName(name: string): ComponentType {
            if (this[name] && this[name].prototype instanceof Component) {
                return this[name];
            }
        }
    }
    
    function factory(type: components.ComponentType | string): T {
        return typeof type === "string" ?
            new (components.forName(type))() as T:
            new type();
    }
    

    (code in playground)

    If you're going with this approach then you need to make sure that all the component classes are exported.

    (3) Use eval

    class Component {}
    class MyComponent1 extends Component {}
    class MyComponent2 extends Component {}
    
    type ComponentType = {
        new(): T;
    }
    
    function factory(type: ComponentType | string): T {
        return typeof type === "string" ?
            new (eval(type))() as T:
            new type();
    }
    

    (code in playground)

    This isn't a recommended approach, and you can read all about the cons in using eval in a lot of places.
    But it's still an option so I'm listing it.

提交回复
热议问题