web-dev-qa-db-fra.com

Les avantages d'utiliser prototype, vs définir des méthodes directement dans le constructeur?

Je me demande s'il y a un avantage à utiliser l'un ou l'autre de ceux-ci, et quelle voie devrais-je prendre?

Approche constructeur:

var Class = function () {

    this.calc = function (a, b) {
        return a + b;
    };

};

Approche prototype:

var Class = function () {};

Class.prototype.calc = function (a, b) {
    return a + b;
};

Je n'aime pas ça. En utilisant le prototype, les définitions de méthodes sont séparées de la classe, et je ne sais pas s'il existe une raison spécifique pour laquelle je devrais utiliser cela uniquement lors de la première approche.

En outre, l'utilisation d'un littéral de fonction pour définir une "classe" présente-t-elle un avantage par rapport à la définition de la fonction:

var Class = function () {};

contre

function Class () {};

Merci!

290
Leo

Les méthodes héritées via la chaîne de prototypes peuvent être modifiées universellement pour toutes les instances, par exemple:

function Class () {}
Class.prototype.calc = function (a, b) {
    return a + b;
}

// Create 2 instances:
var ins1 = new Class(),
    ins2 = new Class();

// Test the calc method:
console.log(ins1.calc(1,1), ins2.calc(1,1));
// -> 2, 2

// Change the prototype method
Class.prototype.calc = function () {
    var args = Array.prototype.slice.apply(arguments),
        res = 0, c;

    while (c = args.shift())
        res += c;

    return res; 
}

// Test the calc method:
console.log(ins1.calc(1,1,1), ins2.calc(1,1,1));
// -> 3, 3

Remarquez comment changer la méthode appliquée aux deux instances? En effet, ins1 et ins2 partagent la même fonction calc(). Pour ce faire avec les méthodes publiques créées lors de la construction, vous devez affecter la nouvelle méthode à chaque instance créée, ce qui est une tâche fastidieuse. En effet, ins1 et ins2 auraient leurs propres fonctions calc() créées individuellement.

Un autre effet secondaire de la création de méthodes dans le constructeur est une performance plus médiocre. Chaque méthode doit être créée à chaque exécution de la fonction constructeur. Les méthodes de la chaîne de prototypes sont créées une fois, puis "héritées" par chaque instance. D'un autre côté, les méthodes publiques ont accès à des variables "privées", ce qui n'est pas possible avec les méthodes héritées.

En ce qui concerne votre question function Class() {} vs var Class = function () {}, la première est "hissée" au sommet de la portée actuelle avant exécution. Pour ces derniers, la déclaration de variable est levée, mais pas l'affectation. Par exemple:

// Error, fn is called before the function is assigned!
fn();
var fn = function () { alert("test!"); } 

// Works as expected: the fn2 declaration is hoisted above the call
fn2();
function fn2() { alert("test!"); }
447
Andy E

L'avantage de l'approche prototype est l'efficacité. Il y a un objet calc() function partagé entre tous les objets Class (j'entends par là des objets créés en appelant le constructeur Class,). L'autre façon (attribuer des méthodes dans le constructeur) crée un nouvel objet fonction pour chaque objet Class, en utilisant davantage de mémoire et en prenant plus de temps de traitement lors de l'appel du constructeur Class. Cependant, cette approche présente un avantage: la méthode calc() a accès aux variables locales du constructeur que vous pouvez utiliser à votre avantage:

function Class() {
    var calcCallCount = 0;

    this.calc = function (a, b) {
        ++calcCallCount;
        alert("Calc called " + calcCallCount + " times");
        return a + b;
    };
};

En ce qui concerne var Class = function() {...} par rapport à function Class() {...}, je préfère généralement cette dernière solution parce que cela signifie que la fonction a un nom, ce qui peut être utile lors du débogage. L’autre différence est que la dernière version (une déclaration de fonction ) est levée, ce qui signifie qu’elle est disponible partout dans le champ dans lequel elle est définie, et non juste après la définition. Cependant, certaines personnes préfèrent utiliser l'ancienne (une expression de fonction ) partout.

64
Tim Down
var YourClass = function(){
  var privateField = "somevalue";
  this.publicField = "somevalue";
  this.instanceMethod1 = function(){
     //you may access both private and public field from here:
     //in order to access public field, you must use "this":
     alert(privateField + "; " + this.publicField);
  };
}

YourClass.prototype.instanceMethod2 = function(){
  //you may access only public field 2 from this method, but not private fields:
  alert(this.publicField);
  //error: drawaback of prototype methods:
  alert(privateField);  
};

Avantages des méthodes prototypes:

  1. Lorsque vous définissez des méthodes via un prototype, elles sont partagées entre toutes les instances YourClass. Par conséquent, la taille totale de ces instances est inférieure à <si vous définissez des méthodes dans le constructeur; Il existe des tests qui montrent comment la définition de la méthode via un prototype diminue la taille totale de la page html et, par conséquent, la rapidité de son chargement.

  2. un autre avantage des méthodes, définies via prototype, est que lorsque vous utilisez des classes héritées, vous pouvez les écraser. Dans la méthode surchargée de la classe dérivée, vous pouvez appeler la méthode de la classe de base avec le même nom, mais avec des méthodes définies dans le constructeur. tu ne peux pas faire ça.

42
Alexandr