Wie kann ich ein Objekt mit einem Array von Werten für Parameter, anstatt Auflistung sie aus, in JavaScript konstruieren?

StackOverflow https://stackoverflow.com/questions/813383

Frage

Ist das möglich? Ich bin eine einzige Basis Fabrik Funktion zu schaffen, um Fabriken verschiedener Typen zu fahren (aber einige Ähnlichkeiten haben) und ich möchte Argumente in der Lage sein zu passieren als ein Array an der Basis Fabrik, die dann möglicherweise eine Instanz eines neuen Objekts erzeugt, die Argumente zu bevölkern der Konstruktor der entsprechenden Klasse über ein Array.

In JavaScript ist es möglich, eine Anordnung zu verwenden, um eine Funktion mit mehreren Argumenten aufrufen, indem Sie die Anwendung Methode:

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);

Es scheint nicht, als ob es überhaupt ist eine Instanz eines Objekts zu erstellen, obwohl Verwendung gelten, oder?

So etwas (das nicht funktioniert):

var instance = new MyClass.apply(namespace, r);
War es hilfreich?

Lösung

Versuchen Sie folgendes:

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

All Schlüsselwort „new“ tut, ist an den Konstruktor in einem neuen Objekt übergeben, die dann die diese Variable in der Konstruktorfunktion wird.

Je nachdem, wie der Konstruktor geschrieben wurde, können Sie dies tun:

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

Update: Ein Kommentar sagt, das funktioniert nicht, wenn es ein Prototyp. Versuchen Sie dies.

function newApply(class, args) {
    function F() {
        return class.apply(this, args);
    }
    F.prototype = class.prototype;
    return new F();
}

newApply( MyClass, args);

Andere Tipps

Beachten Sie, dass

  • new myClass()
    

    ohne Argumente kann fehlschlagen, da der Konstruktor-Funktion kann auf das Vorhandensein von Argumenten verlassen.

  • myClass.apply(something, args)
    

    wird in vielen Fällen scheitert, vor allem, wenn auf nativen Klassen wie Datum oder Ort genannt.

Ich weiß, dass „eval ist böse“, aber in diesem Fall können Sie Folgendes versuchen:

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;
}

So einfach ist das.

(Es funktioniert genauso wie Instance.New in dieser Blog-Post )

Hacks sind Hacks Hacks sind, aber das ist vielleicht ein etwas eleganter als einige der anderen, da Syntax aufrufen würde ähnlich sein, was Sie wollen, und Sie würden nicht alle ursprünglichen Klassen ändern müssen:

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;
};

Jetzt können Sie entweder von diesen tun, ein Objekt zu erstellen:

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

Zugegeben, wie einige können nicht die Funktion Objekt Prototyp zu modifizieren, so dass Sie es auf diese Weise tun und es als eine Funktion verwenden, anstatt:

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;
};

Und dann würden Sie es wie so nennen:

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

Also, ich hoffe, diejenigen zu jemandem nützlich sind - sie ermöglichen es Ihnen, die originalen Konstruktorfunktionen allein zu lassen und bekommen, was Sie nach einer einfachen Code-Zeile sind (im Gegensatz zu den beiden Linien müssen Sie für die aktuell gewählte Lösung / Abhilfe .

Feedback ist willkommen und geschätzt.


UPDATE: Eine andere Sache zu beachten - versuchen Instanzen des gleichen Typs mit diesen verschiedenen Methoden erstellen und dann, wenn ihre Konstruktor Eigenschaften gleich sind zu überprüfen, - Sie können wollen, dass der Fall sein, wenn Sie jemals das müssen prüfen Typ eines Objekts. Was ich meine, wird am besten durch den folgenden Code dargestellt:

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);

Versuchen Sie das mal mit einigen der anderen Hacks und sehen, was passiert. Idealerweise sollten Sie sie vom gleichen Typ sein.


CAVEAT: Wie in den Kommentaren erwähnt, wird dies nicht für die Konstruktorfunktionen arbeiten, die anonyme Funktion Syntax erstellt verwenden, das heißt

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

Es sei denn, Sie sie wie folgt erstellen:

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

Die Lösung, die ich oben bereitgestellt wird, kann oder nicht für Sie nützlich sein können, müssen Sie genau verstehen, was Sie an der besten Lösung für Ihre speziellen Bedürfnisse zu gelangen tun, und Sie müssen bewusst sein, was los ist zu machen meine Lösung „zu arbeiten.“ Wenn Sie nicht verstehen, wie meine Lösung funktioniert, verbringt viel Zeit, es herauszufinden.


alternative Lösung: Nicht ein einziger zu anderen Optionen übersehen, ist hier eine der anderen Möglichkeiten, wie Sie diese Katze (mit ähnlichen Einschränkungen der obigen Ansatz) Haut könnte dies ein etwas esoterisch:

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;
};

Genießen Sie!

was ist das Problem zu umgehen?

function MyClass(arg1, arg2) {

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

    init(arg1, arg2);
}

Wie können Sie:

var obj = new MyClass();
obj.apply(obj, args);

Eine Möglichkeit ist, den Konstruktor Arbeit wie ein normaler Funktionsaufruf zu machen.

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

    // normal constructor here
}

Der Zustand auf der if Aussage wird wahr, wenn Sie MyClass als eine normale Funktion (einschließlich mit call / apply solange das this Argument kein MyClass object ist) an.

Nun sind alle diese sind äquivalent:

new MyClass(arg1, arg2);
MyClass(arg1, arg2);
MyClass.call(null, arg1, arg2);
MyClass.apply(null, [arg1, arg2]);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top