web-dev-qa-db-fra.com

Utilisation de 'prototype' contre 'this' en JavaScript?

Quelle est la différence entre

var A = function () {
    this.x = function () {
        //do something
    };
};

et

var A = function () { };
A.prototype.x = function () {
    //do something
};
760
sw234

Les exemples ont des résultats très différents.

Avant d'examiner les différences, il convient de noter les points suivants:

  • Le constructeur prototype permet de partager des méthodes et des valeurs entre instances via la propriété privée [[Prototype]] de l'instance.
  • Une fonction this est définie par la méthode utilisée pour appeler la fonction ou par l'utilisation de bind (non décrit ici). Lorsqu'une fonction est appelée sur un objet (par exemple, myObj.method()), alors this dans la méthode fait référence à l'objet. Où this n'est pas défini par l'appel ni par l'utilisation de bind , il s'agit par défaut de l'objet global (fenêtre dans un navigateur ) ou en mode strict, reste indéfini.
  • JavaScript est un langage orienté objet, c’est-à-dire que la plupart des valeurs sont des objets, y compris des fonctions. (Les chaînes, les nombres et les booléens sont des not objets.)

Alors voici les extraits en question:

var A = function () {
    this.x = function () {
        //do something
    };
};

Dans ce cas, la variable A reçoit une valeur qui fait référence à une fonction. Lorsque cette fonction est appelée à l'aide de A(), la fonction this n'est pas définie par l'appel; elle est donc définie par défaut sur l'objet global et l'expression this.x est efficace window.x. Le résultat est qu'une référence à l'expression de fonction sur le côté droit est affectée à window.x.

Dans le cas de:

var A = function () { };
A.prototype.x = function () {
    //do something
};

quelque chose de très différent se produit. Dans la première ligne, la référence à une fonction est attribuée à la variable A. En JavaScript, tous les objets de fonction ont par défaut une propriété prototype ; il n’existe donc pas de code distinct pour créer un objet A.prototype .

Dans la deuxième ligne, A.prototype.x se voit attribuer une référence à une fonction. Cela créera une propriété x si elle n'existe pas ou assignera une nouvelle valeur si elle existe. Ainsi, la différence avec le premier exemple dans lequel la propriété de l'objet x est impliquée dans l'expression.

Un autre exemple est ci-dessous. C'est semblable au premier (et peut-être ce que vous vouliez demander):

var A = new function () {
    this.x = function () {
        //do something
    };
};

Dans cet exemple, l'opérateur new a été ajouté avant l'expression de la fonction afin que celle-ci soit appelée en tant que constructeur. Lorsqu'elle est appelée avec new, la fonction this est définie pour référencer un nouvel objet dont la propriété privée [[Prototype]] est définie pour faire référence au public du constructeur prototype . Ainsi, dans l'instruction d'affectation, la propriété x sera créée sur ce nouvel objet. Lorsqu'elle est appelée en tant que constructeur, une fonction retourne son objet this ==; il n'est donc pas nécessaire de disposer d'une instruction séparée return this;.

Pour vérifier que A a une propriété x :

console.log(A.x) // function () {
                 //   //do something
                 // };

Ceci est une utilisation peu commune de new == puisque le seul moyen de référencer le constructeur est via A.constructor . Il serait beaucoup plus courant de faire:

var A = function () {
    this.x = function () {
        //do something
    };
};
var a = new A();

Un autre moyen d'obtenir un résultat similaire consiste à utiliser une expression de fonction immédiatement appelée:

var A = (function () {
    this.x = function () {
        //do something
    };
}());

Dans ce cas, A a attribué la valeur de retour de l'appel de la fonction à droite. Là encore, puisque this n'est pas défini dans l'appel, l'objet global sera référencé et this.x sera effectif window.x. Puisque la fonction ne renvoie rien, A aura une valeur de undefined.

Ces différences entre les deux approches se manifestent également si vous sérialisez et dé-sérialisez vos objets Javascript vers/à partir de JSON. Les méthodes définies sur le prototype d'un objet ne sont pas sérialisées lorsque vous sérialisez l'objet, ce qui peut s'avérer pratique lorsque vous souhaitez, par exemple, sérialiser uniquement les parties de données d'un objet, mais pas ses méthodes:

var A = function () { 
    this.objectsOwnProperties = "are serialized";
};
A.prototype.prototypeProperties = "are NOT serialized";
var instance = new A();
console.log(instance.prototypeProperties); // "are NOT serialized"
console.log(JSON.stringify(instance)); 
// {"objectsOwnProperties":"are serialized"} 

Questions connexes :

Note: Il peut ne pas y avoir d'économie de mémoire significative entre les deux approches, mais l'utilisation du prototype pour partager des méthodes et des propriétés utilisera probablement moins de mémoire que chaque instance. avoir sa propre copie.

JavaScript n'est pas un langage de bas niveau. Il n’est peut-être pas très utile de penser au prototypage ou à d’autres modèles d’héritage pour modifier explicitement l’allocation de la mémoire.

455
keparo

Comme d'autres l'ont dit dans la première version, l'utilisation de "this" a pour résultat que chaque instance de la classe A possède sa propre copie indépendante de la méthode de fonction "x". Considérant que l'utilisation de "prototype" signifiera que chaque instance de classe A utilisera la même copie de la méthode "x".

Voici du code pour montrer cette différence subtile:

// x is a method assigned to the object using "this"
var A = function () {
    this.x = function () { alert('A'); };
};
A.prototype.updateX = function( value ) {
    this.x = function() { alert( value ); }
};

var a1 = new A();
var a2 = new A();
a1.x();  // Displays 'A'
a2.x();  // Also displays 'A'
a1.updateX('Z');
a1.x();  // Displays 'Z'
a2.x();  // Still displays 'A'

// Here x is a method assigned to the object using "prototype"
var B = function () { };
B.prototype.x = function () { alert('B'); };

B.prototype.updateX = function( value ) {
    B.prototype.x = function() { alert( value ); }
}

var b1 = new B();
var b2 = new B();
b1.x();  // Displays 'B'
b2.x();  // Also displays 'B'
b1.updateX('Y');
b1.x();  // Displays 'Y'
b2.x();  // Also displays 'Y' because by using prototype we have changed it for all instances

Comme d'autres l'ont mentionné, il existe différentes raisons de choisir l'une ou l'autre méthode. Mon échantillon est juste destiné à démontrer clairement la différence.

230
Benry

Prenons ces 2 exemples:

var A = function() { this.hey = function() { alert('from A') } };

vs.

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

La plupart des gens ici (en particulier les réponses les mieux notées) ont essayé d'expliquer en quoi ils sont différents sans expliquer POURQUOI. Je pense que c'est faux et si vous comprenez d'abord les principes fondamentaux, la différence deviendra évidente. Essayons d'abord d'expliquer les principes fondamentaux ...

a) Une fonction est un objet en JavaScript. CHAQUE objet en JavaScript obtient une propriété interne (autrement dit, vous ne pouvez y accéder comme d’autres propriétés, sauf peut-être dans des navigateurs tels que Chrome), souvent appelé __proto__ (vous pouvez taper anyObject.__proto__ dans _.Chrome pour voir ce qu'il référence. C'est juste une propriété, rien de plus. Une propriété en JavaScript = une variable à l'intérieur d'un objet, rien de plus. Que font les variables? Elles pointent vers des choses.

Alors, qu'est-ce que cette propriété __proto__ pointe vers? Eh bien, généralement un autre objet (nous expliquerons pourquoi plus tard). Le seul moyen de forcer JavaScript pour que la propriété __proto__ ne pointe PAS vers un autre objet consiste à utiliser var newObj = Object.create(null). Même si vous faites cela, la propriété __proto__ STILL existe en tant que propriété de l'objet, mais elle ne pointe pas vers un autre objet, elle pointe sur null.

Voici où la plupart des gens sont confus:

Lorsque vous créez une nouvelle fonction en JavaScript (qui est également un objet, n'oubliez pas?), Dès qu'elle est définie, JavaScript crée automatiquement une nouvelle propriété sur cette fonction appelée prototype. Essayez le:

var A = [];
A.prototype // undefined
A = function() {}
A.prototype // {} // got created when function() {} was defined

A.prototype est TOTALEMENT DIFFÉRENT de la propriété __proto__. Dans notre exemple, 'A' a maintenant deux propriétés appelées 'prototype' et __proto__. C'est une grande confusion pour les gens. Les propriétés prototype et __proto__ ne sont aucunement liées, ce sont des éléments distincts pointant vers des valeurs distinctes.

Vous vous demandez peut-être pourquoi le code JavaScript a-t-il la propriété __proto__ créée sur chaque objet? Eh bien, un mot: délégation . Lorsque vous appelez une propriété sur un objet et que celui-ci ne l'a pas, JavaScript recherche l'objet qui est référencé par __proto__ pour voir s'il le possède peut-être. S'il ne l'a pas, alors il regarde la propriété __proto__ de cet objet et ainsi de suite ... jusqu'à la fin de la chaîne. Ainsi, le nom chaîne de prototypes . Bien sûr, si __proto__ ne pointe pas sur un objet mais sur null, bonne chance, JavaScript le réalise et vous retournera undefined pour la propriété.

Vous vous demandez peut-être également pourquoi JavaScript crée une propriété appelée prototype pour une fonction lorsque vous définissez la fonction? Parce qu'il essaie de vous tromper, oui vous tromper qu'il fonctionne comme un langage basé sur les classes.

Continuons avec notre exemple et créons un "objet" à partir de A:

var a1 = new A();

Il se passe quelque chose en arrière-plan quand c'est arrivé. a1 est une variable ordinaire à laquelle un nouvel objet vide a été attribué.

Le fait que vous ayez utilisé l'opérateur new avant un appel de fonction A() a effectué une opération ADDITIONAL en arrière-plan. Le mot clé new a créé un nouvel objet, qui fait maintenant référence à a1 et cet objet est vide. Voici ce qui se passe en plus:

Nous avons dit que dans chaque définition de fonction, une nouvelle propriété créée appelée prototype (à laquelle vous pouvez y accéder, contrairement à la propriété __proto__) a été créée? Eh bien, cette propriété est utilisée maintenant.

Nous sommes donc maintenant au point où nous avons un objet vide a1 fraîchement cuit. Nous avons dit que tous les objets en JavaScript ont une propriété interne __proto__ qui pointe vers quelque chose (a1 l'a aussi), que ce soit null ou un autre objet. L’opérateur new c’est qu’il définit cette propriété __proto__ pour qu'elle pointe sur la propriété prototype de la fonction. Lisez cela à nouveau. C'est fondamentalement ceci:

a1.__proto__ = A.prototype;

Nous avons dit que A.prototype n'est rien d'autre qu'un objet vide (à moins que nous ne le changions en autre chose avant de définir a1). Donc maintenant, fondamentalement, a1.__proto__ pointe vers la même chose que A.prototype pointe vers, qui est cet objet vide. Ils pointent tous deux vers le même objet qui a été créé lorsque cette ligne s'est produite:

A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}

Maintenant, il se passe autre chose lorsque l'instruction var a1 = new A() est traitée. Fondamentalement, A() est exécuté et si A ressemble à ceci:

var A = function() { this.hey = function() { alert('from A') } };

Tout ce qui se trouve à l'intérieur de function() { } va s'exécuter. Lorsque vous atteignez la ligne this.hey.., this devient a1 et vous obtenez ceci:

a1.hey = function() { alert('from A') }

Je ne couvrirai pas pourquoi this change en a1 mais c'est une excellente réponse pour en savoir plus.

Donc, pour résumer, lorsque vous faites var a1 = new A(), il se passe 3 choses en arrière-plan:

  1. Un tout nouvel objet vide est créé et attribué à a1. a1 = {}
  2. La propriété a1.__proto__ est affectée à un point identique à celui de A.prototype (un autre objet vide {})

  3. La fonction A() est en cours d'exécution avec this sur le nouvel objet vide créé à l'étape 1 (lisez la réponse que j'ai citée plus haut pour expliquer pourquoi this devient a1)

Maintenant, essayons de créer un autre objet:

var a2 = new A();

Les étapes 1,2,3 se répètent. Avez-vous remarqué quelque chose? La clé Word est répétée . Étape 1: a2 sera un nouvel objet vide, étape 2: sa propriété __proto__ sera pointez sur la même chose que A.prototype pointe sur et surtout, étape 3: la fonction A() est AGAIN à nouveau exécutée, ce qui signifie que a2 obtiendra la propriété hey contenant une fonction. a1 et a2 ont deux propriétés SEPARATE nommées hey qui désignent 2 fonctions SEPARATE! Nous avons maintenant des fonctions en double dans deux objets différents faisant la même chose, oups ... Vous pouvez en imaginer les implications pour la mémoire si nous avons 1000 objets créés avec new A, après que toutes les déclarations de fonctions utilisent plus de mémoire que quelque chose comme le nombre 2. Alors, comment pouvons-nous empêcher cela?

Rappelez-vous pourquoi la propriété __proto__ existe sur chaque objet? Ainsi, si vous récupérez la propriété yoMan sur a1 (qui n'existe pas), sa propriété __proto__ sera consultée, ce qui sera le cas s'il s'agit d'un objet (et dans la plupart des cas). , il vérifiera s'il contient yoMan, et s'il ne le fait pas, il consultera le __proto__ de cet objet, etc. Si tel est le cas, il prendra cette valeur de propriété et vous l'affichera.

Donc quelqu'un a décidé d'utiliser ce fait + le fait que lorsque vous créez a1, sa propriété __proto__ pointe sur le même objet (vide) A.prototype pointe sur et fait ceci:

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

Cool! Maintenant, lorsque vous créez a1, il répète toutes les 3 étapes ci-dessus et à l'étape 3, il ne fait rien, car function A() n'a rien à exécuter. Et si nous le faisons:

a1.hey

Il verra que a1 ne contient pas hey et il vérifiera son objet de propriété __proto__ pour voir s'il en a un, ce qui est le cas.

Avec cette approche, nous éliminons la partie de l'étape 3 dans laquelle les fonctions sont dupliquées à chaque création d'objet. Au lieu de a1 et a2 ayant une propriété séparée hey, AUCUN d'entre eux ne l'a maintenant. Ce qui, je suppose, vous l'avez compris vous-même. C'est la bonne chose ... si vous comprenez __proto__ et Function.prototype, de telles questions seront assez évidentes.

REMARQUE: certaines personnes ont tendance à ne pas appeler la propriété interne Prototype sous le nom __proto__. J'ai utilisé ce nom par le biais du message pour le distinguer clairement de la propriété Functional.prototype de deux manières différentes.

143
daremkd

Dans la plupart des cas, ils sont essentiellement identiques, mais la deuxième version économise de la mémoire car il n'existe qu'une seule instance de la fonction au lieu d'une fonction distincte pour chaque objet.

Une des raisons d'utiliser le premier formulaire est d'accéder aux "membres privés". Par exemple:

var A = function () {
    var private_var = ...;

    this.x = function () {
        return private_var;
    };

    this.setX = function (new_x) {
        private_var = new_x;
    };
};

En raison des règles de portée de javascript, private_var est disponible pour la fonction attribuée à this.x, mais pas en dehors de l'objet.

58
Matthew Crumley

Le premier exemple modifie l'interface pour cet objet uniquement. Le deuxième exemple modifie l'interface pour tous les objets de cette classe.

27
Glenn

Le problème ultime lié à l'utilisation de this au lieu de prototype est que, lors du remplacement d'une méthode, le constructeur de la classe de base fera toujours référence à la méthode remplacée. Considère ceci:

BaseClass = function() {
    var text = null;

    this.setText = function(value) {
        text = value + " BaseClass!";
    };

    this.getText = function() {
        return text;
    };

    this.setText("Hello"); // This always calls BaseClass.setText()
};

SubClass = function() {
    // setText is not overridden yet,
    // so the constructor calls the superclass' method
    BaseClass.call(this);

    // Keeping a reference to the superclass' method
    var super_setText = this.setText;
    // Overriding
    this.setText = function(value) {
        super_setText.call(this, "SubClass says: " + value);
    };
};
SubClass.prototype = new BaseClass();

var subClass = new SubClass();
console.log(subClass.getText()); // Hello BaseClass!

subClass.setText("Hello"); // setText is already overridden
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

contre:

BaseClass = function() {
    this.setText("Hello"); // This calls the overridden method
};

BaseClass.prototype.setText = function(value) {
    this.text = value + " BaseClass!";
};

BaseClass.prototype.getText = function() {
    return this.text;
};

SubClass = function() {
    // setText is already overridden, so this works as expected
    BaseClass.call(this);
};
SubClass.prototype = new BaseClass();

SubClass.prototype.setText = function(value) {
    BaseClass.prototype.setText.call(this, "SubClass says: " + value);
};

var subClass = new SubClass();
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

Si vous pensez que ce n'est pas un problème, cela dépend si vous pouvez vivre sans variables privées et si vous êtes suffisamment expérimenté pour connaître une fuite lorsque vous en voyez une. Aussi, avoir à mettre la logique du constructeur après les définitions de la méthode est peu pratique.

var A = function (param1) {
    var privateVar = null; // Private variable

    // Calling this.setPrivateVar(param1) here would be an error

    this.setPrivateVar = function (value) {
        privateVar = value;
        console.log("setPrivateVar value set to: " + value);

        // param1 is still here, possible memory leak
        console.log("setPrivateVar has param1: " + param1);
    };

    // The constructor logic starts here possibly after
    // many lines of code that define methods

    this.setPrivateVar(param1); // This is valid
};

var a = new A(0);
// setPrivateVar value set to: 0
// setPrivateVar has param1: 0

a.setPrivateVar(1);
//setPrivateVar value set to: 1
//setPrivateVar has param1: 0

contre:

var A = function (param1) {
    this.setPublicVar(param1); // This is valid
};
A.prototype.setPublicVar = function (value) {
    this.publicVar = value; // No private variable
};

var a = new A(0);
a.setPublicVar(1);
console.log(a.publicVar); // 1
21
tarkabak

Chaque objet est lié à un objet prototype. Lorsque vous tentez d'accéder à une propriété qui n'existe pas, JavaScript recherchera cette propriété dans l'objet prototype de l'objet et la retournera s'il existe.

La propriété prototype d'un constructeur de fonction fait référence à l'objet prototype de toutes les instances créées avec cette fonction lors de l'utilisation de new.


Dans votre premier exemple, vous ajoutez une propriété x à chaque instance créée avec la fonction A.

var A = function () {
    this.x = function () {
        //do something
    };
};

var a = new A();    // constructor function gets executed
                    // newly created object gets an 'x' property
                    // which is a function
a.x();              // and can be called like this

Dans le deuxième exemple, vous ajoutez une propriété à l'objet prototype sur laquelle toutes les instances créées avec A.

var A = function () { };
A.prototype.x = function () {
    //do something
};

var a = new A();    // constructor function gets executed
                    // which does nothing in this example

a.x();              // you are trying to access the 'x' property of an instance of 'A'
                    // which does not exist
                    // so JavaScript looks for that property in the prototype object
                    // that was defined using the 'prototype' property of the constructor

En conclusion, dans le premier exemple ne copie de la fonction est assignée à chaque instance. Dans le deuxième exemple ne seule copie de la fonction est partagée par toutes les instances.

20
destoryer

Quelle est la différence? => Beaucoup.

Je pense que la version this est utilisée pour activer l’encapsulation, c’est-à-dire le masquage de données. Cela aide à manipuler des variables privées.

Regardons l'exemple suivant:

var AdultPerson = function() {

  var age;

  this.setAge = function(val) {
    // some Housekeeping
    age = val >= 18 && val;
  };

  this.getAge = function() {
    return age;
  };

  this.isValid = function() {
    return !!age;
  };
};

Maintenant, la structure prototype peut être appliquée comme suit:

Différents adultes ont des âges différents, mais tous les adultes ont les mêmes droits.
Nous l’ajoutons donc en utilisant un prototype plutôt que celui-ci.

AdultPerson.prototype.getRights = function() {
  // Should be valid
  return this.isValid() && ['Booze', 'Drive'];
};

Regardons la mise en œuvre maintenant.

var p1 = new AdultPerson;
p1.setAge(12); // ( age = false )
console.log(p1.getRights()); // false ( Kid alert! )
p1.setAge(19); // ( age = 19 )
console.log(p1.getRights()); // ['Booze', 'Drive'] ( Welcome AdultPerson )

var p2 = new AdultPerson;
p2.setAge(45);    
console.log(p2.getRights()); // The same getRights() method, *** not a new copy of it ***

J'espère que cela t'aides.

16
oozzal

Le prototype est le modèle de la classe; qui s'applique à toutes les instances futures de celui-ci. Alors que c'est l'instance particulière de l'objet.

14
harropriiz

Laissez-moi vous donner une réponse plus complète que j'ai apprise lors d'un cours de formation sur JavaScript.

La plupart des réponses mentionnaient déjà la différence, c’est-à-dire que lors du prototypage, la fonction était partagée avec toutes les instances (futures). Tandis que déclarer la fonction dans la classe créera une copie pour chaque instance.

En général, il n’ya ni bon ni mauvais, c’est davantage une question de goût ou de décision de conception en fonction de vos besoins. Cependant, le prototype est la technique utilisée pour développer une approche orientée objet, comme j'espère que vous verrez à la fin de cette réponse.

Vous avez montré deux modèles dans votre question. Je vais essayer d’en expliquer deux autres et d’expliquer les différences, le cas échéant. N'hésitez pas à éditer/étendre. Dans tous les exemples, il s'agit d'un objet de voiture qui a un emplacement et peut se déplacer.

Motif décorateur d'objet

Vous ne savez pas si ce modèle est toujours d'actualité, mais il existe. Et il est bon de savoir à ce sujet. Vous passez simplement un objet et une propriété à la fonction décorateur. Le décorateur renvoie l'objet avec la propriété et la méthode.

var carlike = function(obj, loc) {
    obj.loc = loc;
    obj.move = function() {
        obj.loc++;
    };
    return obj;
};

var amy = carlike({}, 1);
amy.move();
var ben = carlike({}, 9);
ben.move();

Classes fonctionnelles

Une fonction en JavaScript est un objet spécialisé. En plus d'être appelée, une fonction peut stocker des propriétés comme n'importe quel autre objet.

Dans ce cas, Car est un fonction (aussi think objet) qui peut être appelé comme vous le faites habituellement. Il a une propriété methods (qui est un objet avec une fonction move). Lorsque Car est appelé, la fonction extend est appelée, ce qui permet d'effectuer des opérations magiques et étend la fonction Car (objet think) aux méthodes définies dans methods.

Cet exemple, bien que différent, se rapproche le plus du premier exemple de la question.

var Car = function(loc) {
    var obj = {loc: loc};
    extend(obj, Car.methods);
    return obj;
};

Car.methods = {
    move : function() {
        this.loc++;
    }
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

Classes Prototypiques

Les deux premiers modèles permettent de discuter de l’utilisation de techniques pour définir des méthodes partagées ou de méthodes définies en ligne dans le corps du constructeur. Dans les deux cas, chaque instance a sa propre fonction move.

Le modèle prototypal ne se prête pas bien au même examen, car le partage de fonctions via une délégation de prototype est l’objet même du modèle prototypal. Comme d'autres l'ont souligné, la mémoire devrait être meilleure.

Cependant, il y a un point intéressant à savoir: chaque objet prototype a une propriété de commodité constructor, qui renvoie à la fonction (objet de réflexion) à laquelle il est attaché.

Concernant les trois dernières lignes:

Dans cet exemple, Car est lié à l'objet prototype, qui relie via constructor à Car lui-même, c'est-à-dire Car.prototype.constructor est Car lui-même. Cela vous permet de déterminer quelle fonction de constructeur a construit un certain objet.

La recherche de amy.constructor échoue et est donc déléguée à Car.prototype, qui possède la propriété constructeur. Et ainsi amy.constructor est Car.

De plus, amy est un instanceofCar. L'opérateur instanceof fonctionne en vérifiant si l'objet prototype de l'opérande droit (Car) peut être trouvé n'importe où dans la chaîne de prototypes de l'opérande gauche (amy).

var Car = function(loc) {
    var obj = Object.create(Car.prototype);
    obj.loc = loc;
    return obj;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

console.log(Car.prototype.constructor);
console.log(amy.constructor);
console.log(amy instanceof Car);

Certains développeurs peuvent être confus au début. Voir ci-dessous exemple:

var Dog = function() {
  return {legs: 4, bark: alert};
};

var fido = Dog();
console.log(fido instanceof Dog);

L'opérateur instanceof renvoie false, car le prototype de Dog ne peut être trouvé nulle part dans la chaîne de prototypes de fido. fido est un objet simple créé avec un littéral d'objet, c'est-à-dire qu'il délègue simplement des délégués à Object.prototype.

Modèles pseudoclassiques

Il ne s’agit en réalité que d’une autre forme du motif prototype, simplifiée et plus familière pour ceux qui programment dans Java par exemple, car ils utilisent le constructeur new.

Il fait la même chose que dans le modèle prototypal, c’est vraiment un sucre syntaxique par-dessus le modèle prototypal.

Cependant, la principale différence réside dans le fait que des optimisations implémentées dans les moteurs JavaScript ne s'appliquent que lors de l'utilisation du modèle pseudoclassique. Pensez que le motif pseudoclassique est une version probablement plus rapide du motif prototype; les relations d'objet dans les deux exemples sont les mêmes.

var Car = function(loc) {
    this.loc = loc;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = new Car(1);
amy.move();
var ben = new Car(9);
ben.move();

Enfin, il ne devrait pas être trop difficile de comprendre comment une programmation orientée objet peut être effectuée. Il y a deux sections.

Une section qui définit les propriétés/méthodes communes dans le prototype (chaîne).

Et une autre section où vous mettez les définitions qui distinguent les objets les uns des autres (loc variable dans les exemples).

C'est ce qui nous permet d'appliquer des concepts tels que superclasse ou sous-classe en JavaScript.

N'hésitez pas à ajouter ou à modifier. Une fois de plus, je pourrais peut-être en faire un wiki de communauté.

13
Ely

Je sais que la réponse à cette question est mortelle, mais j'aimerais montrer un exemple concret de différence de vitesse.

Function directly on object

Function on prototype

Ici, nous créons 2 000 000 nouveaux objets avec une méthode print dans Chrome. Nous stockons chaque objet dans un tableau. Mettre print sur le prototype prend environ 1/2 fois plus longtemps.

13
Arnav Aggarwal

Je crois que @Matthew Crumley a raison. Ils sont fonctionnellement , sinon structurellement, équivalents. Si vous utilisez Firebug pour examiner les objets créés à l'aide de new, vous constatez qu'ils sont identiques. Cependant, ma préférence serait la suivante. J'imagine que cela ressemble plus à ce à quoi je suis habitué en C #/Java. Autrement dit, définissez la classe, définissez les champs, le constructeur et les méthodes.

var A = function() {};
A.prototype = {
    _instance_var: 0,

    initialize: function(v) { this._instance_var = v; },

    x: function() {  alert(this._instance_var); }
};

EDIT Ne voulait pas dire que la portée de la variable était privée, j'essayais simplement d'illustrer la façon dont je définis mes classes en javascript. Le nom de la variable a été modifié pour refléter cela.

11
tvanfosson

Comme indiqué dans d'autres réponses, il s'agit en réalité d'une considération de performance, car la fonction du prototype est partagée avec toutes les instanciations - plutôt que la fonction créée pour chaque instanciation.

Je mets en place un jsperf pour le montrer. Il y a une différence considérable dans le temps nécessaire pour instancier la classe, bien que ce ne soit vraiment pertinent que si vous faites de nombreuses instances.

http://jsperf.com/functions-in-constructor-vs-prototype

10
Devgr

Pensez au langage de type statique, les choses sur prototype sont statiques et les choses sur this sont liées à des instances.

8
Wayou