Node.js - 创建变量中指定的 class 名称的对象

Node.js - create object of class name specified in variable

我有一个 class 层次结构,例如:

               |-> Square
AbstractShape -+-> Circle
               |-> Triangle

现在,我想实现策略模式并创建一个存储在字符串中的 class 对象。在 PHP 中,我会使用:

$type = 'Square';
$obj = new $type();

在Node.js中有对应的吗?

  1. 快速而肮脏的方法是使用eval。但强烈不推荐,原因有很多——安全、性能、可读性、可支持性

    function MyType() {
    }
    
    var typeName = 'MyType';
    var typeObj = eval('new ' + typeName + '()');
    
  2. eval更安全更正确的是使用字符串名称到类型的映射(感谢@GaloisGecko)

    function createType(name) {
      var types = {
        "Square": Square,
        "Circle": Circle,
        "Triangle": Tringle
      };
      return types.hasOwnProperty(name) ? new types[name]() : null;
    }
    
  3. 最后,最好最明智的决定是应用工厂模式。参见 . Also you can find good description and example here

一个安全的方法是定义一个工厂对象:

function Square() {
}

// here other constructors for Circle and Triangle   

var factory = {
    "Square": Square,
    "Circle": Circle,
    "Triangle" : Triangle   
}

var typeName;

// here some code which sets typeName

var typeObj = new factory[typeName]();

如果您希望采用更健壮和可测试的方法,您可以结合使用 类 和工厂模式来发布对象。查看以下内容,您会发现使用此设置,包括更细粒度的逻辑和测试会变得更容易,并为您提供更大的灵活性。您还在 .issue 调用后面自己抽象出 new-ing 对象 - 在某些情况下这可能是有益和方便的。

我还注意到你提到了你的 PHP 背景,所以我也展示了一些如何在 ES6 中采用面向对象的方法。

class AbstractShape {
  constructor(type) {
    this.type = type;
  }

  getType() {
    console.log(`I am a ${this.type}`);
  }
}

class Square extends AbstractShape {
  constructor(type) {
    super(type);
    this.sides = 4;
  }

  getDescription() {
    console.log(`I have ${this.sides} equal sides`);
  }
}

class ShapeFactory {
  static issue(type) {
    switch(type) {
      case 'Square': return new Square(type);
        break;
      case 'Circle': /* same pattern with a Circle class */
        break;
    }
  }
}

let shape = ShapeFactory.issue('Square');

shape.getType();        /* I am a Square */
shape.getDescription(); /* I have 4 equal sides */

JSFiddle Link - 演示


此外,如果您想要比处理冗余字符串更容错的东西,例如'Square' - there are some creative ways 利用类似枚举的方法进一步完善这一点。我将在此处保留不动产,而不是重新散列代码片段,但会包含一个 fiddle 供您查看。

JSFiddle Link - 枚举方法演示

经过仔细考虑,Node.js 有一个非常简单的解决方法。 当您以最简单的方式实例化一个对象时,您实际上会编写 new <variableName>,其中 variableName 是某个函数的主体,或者 class 在某个模块中定义和导出。要将此 function/class 分配给您 require() 变量。

所以,而不是

const type = 'Square';
const aSquare = new type();

你需要写:

const type = 'Square';
const shape = require(`${pathToShapeModules}/${type}.js`); 
const aShape = new shape();

小缺点是 eslint 抱怨(在某些规则设置中)requires 将被放置在模块的顶部。当然,它需要通过 try...catch 等进行适当的异常处理,所以 Factory 解决方案可能更好(所以我将接受它),但我认为对于小型特殊情况,此解决方案是可以的。