83 votes

Comment puis-je appeler un constructeur de javascript à l’aide d’appel ou appliquer ?

Comment pouvais généraliser la fonction ci-dessous pour accepter des arguments N ? (À l’aide d’appel ou appliquer ?)

Y a-t-il une manière programmatique pour appliquer des arguments aux « nouveaux » ? Je ne veux pas le constructeur pour être traitée comme une simple fonction.

Tous les meilleurs,

Chris.

test de qUnit :

95voto

kybernetikos Points 3127

C'est comment vous le faites:

function applyToConstructor(constructor, argArray) {
    var args = [null].concat(argArray);
    var factoryFunction = constructor.bind.apply(constructor, args);
    return new factoryFunction();
}

var d = applyToConstructor(Date, [2008, 10, 8, 00, 16, 34, 254]);

L'appel est un peu plus facile

function callConstructor(constructor) {
    var factoryFunction = constructor.bind.apply(constructor, arguments);
    return new factoryFunction();
}

var d = callConstructor(Date, 2008, 10, 8, 00, 16, 34, 254);

Vous pouvez utiliser une de ces options pour créer de l'usine de fonctions:

var dateFactory = applyToConstructor.bind(null, Date)
var d = dateFactory([2008, 10, 8, 00, 16, 34, 254]);

ou

var dateFactory = callConstructor.bind(null, Date)
var d = dateFactory(2008, 10, 8, 00, 16, 34, 254);

Il fonctionne avec n'importe quel constructeur, et pas seulement built-ins ou les constructeurs qui peut doubler comme des fonctions (comme la Date).

Cependant, elle nécessite l'Ecmascript 5 .fonction de liaison. Les cales ne sera probablement pas fonctionner correctement.

Une approche différente, plus dans le style de certains des autres réponses est de créer une fonction de la version de la construite en new. Cela ne fonctionnera pas sur tous les objets internes (comme la Date).

function neu(constructor) {
    // http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.2
    var instance = Object.create(constructor.prototype);
    var result = constructor.apply(instance, Array.prototype.slice.call(arguments, 1));

    // The ECMAScript language types are Undefined, Null, Boolean, String, Number, and Object.
    return (result !== null && typeof result === 'object') ? result : instance;
}

function Person(first, last) {this.first = first;this.last = last};
Person.prototype.hi = function(){console.log(this.first, this.last);};

var p = neu(Person, "Neo", "Anderson");

Et maintenant, bien sûr, vous pouvez faire .apply ou .call ou .bind sur neu comme d'habitude.

Par exemple:

var personFactory = neu.bind(null, Person);
var d = personFactory("Harry", "Potter");

Je pense que la première solution que je donne est mieux, car il ne dépend pas de vous correctement de la réplication de la sémantique d'un builtin et qu'il fonctionne correctement avec les builtins.

50voto

James Points 56229

Essaye ça:

 function conthunktor(Constructor) {
    var args = Array.prototype.slice.call(arguments, 1);
    return function() {

         var Temp = function(){}, // temporary constructor
             inst, ret; // other vars

         // Give the Temp constructor the Constructor's prototype
         Temp.prototype = Constructor.prototype;

         // Create a new instance
         inst = new Temp;

         // Call the original Constructor with the temp
         // instance as its context (i.e. its 'this' value)
         ret = Constructor.apply(inst, args);

         // If an object has been returned then return it otherwise
         // return the original instance.
         // (consistent with behaviour of the new operator)
         return Object(ret) === ret ? ret : inst;

    }
}
 

14voto

Jason Orendorff Points 15869

Cette fonction est identique à `` dans tous les cas. Il sera probablement significativement plus lent que de 999 réponse, cependant, donc utilisez-le seulement si vous avez vraiment besoin.

4voto

Fuerteflojo Points 56

Une autre approche, qui nécessite de modifier le constructeur réel appellé, mais semble plus propre pour moi que l’utilisation de eval() ou d’introduire une nouvelle fonction factice dans la chaîne de construction... Garder votre fonction conthunktor comme

Et modifier les constructeurs étant appelés...

Alors vous pouvez essayer :

3voto

Louis Points 13534

En utilisant une temporaire constructeur semble être la meilleure solution si l' Object.create n'est pas disponible.

Si Object.create est disponible, puis à l'aide il est une meilleure option. Sur Node.js, à l'aide de Object.create résultats beaucoup plus rapidement le code. Voici un exemple de la façon dont Object.create peut être utilisé:

function applyToConstructor(ctor, args) {
    var new_obj = Object.create(ctor.prototype);
    var ctor_ret = ctor.apply(new_obj, args);

    // Some constructors return a value; make sure to use it!
    return ctor_ret !== undefined ? ctor_ret: new_obj;
}

(Évidemment, l' args argument est une liste d'arguments à appliquer.)

J'avais un morceau de code qui, à l'origine utilisé eval pour lire un morceau de données créée par un autre outil. (Oui, eval est le mal.) Ce serait instancier un arbre de centaines de milliers d'éléments. Fondamentalement, le moteur JavaScript a été responsable de l'analyse et de l'exécution d'un tas d' new ...(...) expressions. J'ai converti mon système pour analyser une structure JSON, ce qui signifie que je dois avoir mon code de déterminer quel constructeur à appeler pour chaque type d'objet dans l'arbre. Quand j'ai couru le nouveau code dans mon test de la suite, j'ai été surpris de voir un dramatique ralentir par rapport à l' eval version.

  1. Suite de tests avec eval version: 1 seconde.
  2. La suite de tests avec la version JSON, l'aide temporaire constructeur: 5 secondes.
  3. La suite de tests avec la version JSON, à l'aide de Object.create: 1 seconde.

La suite de test crée plusieurs arbres. J'ai calculé mon applytoConstructor fonction a été appelée à propos de 125 000 fois lors de la suite de test est exécuté.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X