代码之家  ›  专栏  ›  技术社区  ›  Bjorn

如何使用参数值数组来构造一个对象,而不是在javascript中列出它们?

  •  11
  • Bjorn  · 技术社区  · 16 年前

    这有可能吗?我正在创建一个基础工厂函数来驱动不同类型的工厂(但有一些相似之处),我希望能够将参数作为数组传递给基础工厂,然后基础工厂可能会创建一个新对象的实例,通过数组填充相关类的构造函数的参数。

    在javascript中,可以使用数组通过apply方法调用具有多个参数的函数:

    namespace.myFunc = function(arg1, arg2) { //do something; }
    var result = namespace.myFunc("arg1","arg2");
    //this is the same as above:
    var r = [ "arg1","arg2" ];
    var result = myFunc.apply(namespace, r);
    

    但是,似乎不存在使用apply创建对象实例的情况,是吗?

    比如(这不管用):

    var instance = new MyClass.apply(namespace, r);
    
    5 回复  |  直到 15 年前
        1
  •  15
  •   Matthew Crumley    16 年前

    试试这个:

    var instance = {};
    MyClass.apply( instance, r);
    

    关键字“new”的所有功能都是将一个新对象传递给构造函数,然后在构造函数函数中成为这个变量。

    根据构造函数的编写方式,您可能需要执行以下操作:

    var instance = {};
    var returned = MyClass.apply( instance, args);
    if( returned != null) {
        instance = returned;
    }
    

    更新:一条评论说,如果有一个原型,这是不起作用的。试试这个。

    function newApply(class, args) {
        function F() {
            return class.apply(this, args);
        }
        F.prototype = class.prototype;
        return new F();
    }
    
    newApply( MyClass, args);
    
        2
  •  2
  •   user123444555621    15 年前

    注意

    • new myClass()
      

      没有任何参数都可能失败,因为构造函数函数可能依赖于参数的存在。

    • myClass.apply(something, args)
      

      在许多情况下都会失败,特别是在日期或数字之类的本机类上调用时。

    我知道“eval是邪恶的”,但在这种情况下,您可能想尝试以下方法:

    function newApply(Cls, args) {
        var argsWrapper = [];
        for (var i = 0; i < args.length; i++) {
            argsWrapper.push('args[' + i + ']');
        }
        eval('var inst = new Cls(' + argsWrapper.join(',') + ');' );
        return inst;
    }
    

    很简单。

    (其工作原理与 Instance.New 在里面 this blog post )

        3
  •  1
  •   Jason Bunting    16 年前

    hacks是hacks,但也许这一个比其他一些更优雅,因为调用语法与您想要的相似,您根本不需要修改原始类:

    Function.prototype.build = function(parameterArray) {
        var functionNameResults = (/function (.{1,})\(/).exec(this.toString());
        var constructorName = (functionNameResults && functionNameResults.length > 1) ? functionNameResults[1] : "";
        var builtObject = null;
        if(constructorName != "") {
           var parameterNameValues = {}, parameterNames = [];
           for(var i = 0; i < parameterArray.length; i++) {
             var parameterName = ("p_" + i);
             parameterNameValues[parameterName] = parameterArray[i];
             parameterNames.push(("parameterNameValues." + parameterName));
           }
           builtObject = (new Function("parameterNameValues", "return new " + constructorName + "(" + parameterNames.join(",") + ");"))(parameterNameValues);
        }
        return builtObject;
    };
    

    现在,您可以执行以下任一操作来构建对象:

    var instance1 = MyClass.build(["arg1","arg2"]);
    var instance2 = new MyClass("arg1","arg2");
    

    当然,有些人可能不喜欢修改函数对象的原型,因此您可以这样做并将其用作函数:

    function build(constructorFunction, parameterArray) {
        var functionNameResults = (/function (.{1,})\(/).exec(constructorFunction.toString());
        var constructorName = (functionNameResults && functionNameResults.length > 1) ? functionNameResults[1] : "";
        var builtObject = null;
        if(constructorName != "") {
           var parameterNameValues = {}, parameterNames = [];
           for(var i = 0; i < parameterArray.length; i++) {
             var parameterName = ("p_" + i);
             parameterNameValues[parameterName] = parameterArray[i];
             parameterNames.push(("parameterNameValues." + parameterName));
           }
           builtObject = (new Function("parameterNameValues", "return new " + constructorName + "(" + parameterNames.join(",") + ");"))(parameterNameValues);
        }
        return builtObject;
    };
    

    然后你会这样称呼它:

    var instance1 = build(MyClass, ["arg1","arg2"]);
    

    因此,我希望这些对某些人有用——它们允许您单独使用原始的构造函数函数,并通过一行简单的代码(不同于当前所选解决方案/解决方案所需的两行代码)获得所需的内容。

    欢迎并感谢您的反馈。


    更新:还有一件事需要注意-尝试用这些不同的方法创建相同类型的实例,然后检查它们的构造函数属性是否相同-如果需要检查对象的类型,您可能希望这样。我的意思最好用下面的代码来说明:

    function Person(firstName, lastName) {
       this.FirstName = firstName;
       this.LastName = lastName;
    }
    
    var p1 = new Person("John", "Doe");
    var p2 = Person.build(["Sara", "Lee"]);
    
    var areSameType = (p1.constructor == p2.constructor);
    

    和其他黑客一起试试看会发生什么。理想情况下,您希望它们是相同的类型。


    警告:正如注释中所指出的,对于那些使用匿名函数语法创建的构造函数函数(即

    MyNamespace.SomeClass = function() { /*...*/ };
    

    除非 您可以这样创建它们:

    MyNamespace.SomeClass = function SomeClass() { /*...*/ };
    

    我上面提供的解决方案可能对您有用,也可能对您不有用,您需要准确地了解您正在做什么,以获得满足您特定需求的最佳解决方案,并且您需要了解将如何使我的解决方案“起作用”。如果您不了解我的解决方案是如何起作用的,请花时间来解决。


    另一种解决方案是:不要忽视其他选择,这里有一个你可以给这只猫剥皮的其他方法(与上述方法类似的警告),这个方法更深奥一点:

    function partial(func/*, 0..n args */) {
       var args = Array.prototype.slice.call(arguments, 1);
       return function() {
          var allArguments = args.concat(Array.prototype.slice.call(arguments));
          return func.apply(this, allArguments);
       };
    }
    
    Function.prototype.build = function(args) {
       var constructor = this;
       for(var i = 0; i < args.length; i++) {
          constructor = partial(constructor, args[i]);
       }
       constructor.prototype = this.prototype;
       var builtObject = new constructor();
       builtObject.constructor = this;
       return builtObject;
    };
    

    享受!

        4
  •  0
  •   Johan Öbrink    16 年前

    工作区怎么样?

    function MyClass(arg1, arg2) {
    
        this.init = function(arg1, arg2){
            //if(arg1 and arg2 not null) do stuff with args
        }
    
        init(arg1, arg2);
    }
    

    所以你可以:

    var obj = new MyClass();
    obj.apply(obj, args);
    
        5
  •  0
  •   Matthew Crumley    16 年前

    一种可能是使构造函数作为普通函数调用工作。

    function MyClass(arg1, arg2) {
        if (!(this instanceof MyClass)) {
            return new MyClass(arg1, arg2);
        }
    
        // normal constructor here
    }
    

    上的条件 if 如果您调用 MyClass 作为正常功能(包括 call / apply 只要 this 参数不是 MyClass object )

    现在所有这些都是等效的:

    new MyClass(arg1, arg2);
    MyClass(arg1, arg2);
    MyClass.call(null, arg1, arg2);
    MyClass.apply(null, [arg1, arg2]);