Utilisation de «prototype» par rapport à «ceci» en JavaScript?

776

Quelle est la différence entre

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

et

var A = function () { };
A.prototype.x = function () {
    //do something
};
sw234
la source
le concept de CE mot-clé est expliqué explicitement ici scotch.io/@alZami/understanding-this-in-javascript
AL-zami
1
La lecture de "ce" fil de discussion montre à quel point JS est horrible et combien ses principes ne sont pas clairs pour de nombreux développeurs. Qu'est-ce qui ne va pas avec des langues plus faciles à comprendre? Je pense qu'il est temps que les développeurs élèvent la voix pour rejeter les technologies déroutantes qui ne servent pas ou peu de valeur aux activités commerciales ou de développement.
NoChance
Sur l'objet a1.x !== a2.x:; sur prototype:a1.x === a2.x
Juan Mendes

Réponses:

467

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

Avant d'examiner les différences, il convient de noter ce qui suit:

  • Un prototype de constructeur permet de partager des méthodes et des valeurs entre des instances via la [[Prototype]]propriété privée de l'instance .
  • Une fonction est c'est définie par la façon dont la fonction est appelée ou par l'utilisation de bind (pas discuté ici). Lorsqu'une fonction est appelée sur un objet (par exemple ), cela dans la méthode fait référence à l'objet. Lorsque cela n'est pas défini par l'appel ou par l'utilisation de bind , il est défini par défaut sur l'objet global (fenêtre dans un navigateur) ou en mode strict, reste indéfini.myObj.method()
  • 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 ne sont pas des objets.)

Voici donc les extraits en question:

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

Dans ce cas, une variable Aest affectée à une variable qui fait référence à une fonction. Lorsque cette fonction est appelée à l' aide A(), la fonction est ce n'est pas définie par l'appel si la valeur par défaut l'objet global et l'expression this.xest 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 variable Ase voit attribuer une référence à une fonction. En JavaScript, tous les objets fonctions ont une propriété prototype par défaut, il n'y a donc pas de code séparé 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. Donc, la différence avec le premier exemple dans lequel la propriété x de l' objet est impliquée dans l'expression.

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

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

Dans cet exemple, l' newopérateur a été ajouté avant l'expression de fonction afin que la fonction 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 [[Prototype]]propriété privée est définie pour référencer le prototype public du constructeur . Ainsi, dans l'instruction d'affectation, la xpropriété sera créée sur ce nouvel objet. Lorsqu'elle est appelée en tant que constructeur, une fonction renvoie son objet this par défaut, il n'est donc pas nécessaire d'avoir une return this;instruction distincte .

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

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

Il s'agit d'une utilisation peu courante de new car la seule façon 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();

Une autre façon d'obtenir un résultat similaire consiste à utiliser une expression de fonction immédiatement invoquée:

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

Dans ce cas, Aaffectez la valeur de retour de l'appel de la fonction sur le côté droit. Là encore, puisque cela n'est pas défini dans l'appel, il fera référence à l'objet global et this.xest effectif window.x. Puisque la fonction ne retourne rien, Aaura 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 / depuis 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 être pratique lorsque, par exemple, vous voulez 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 :

Sidenote: Il n'y a peut-être pas d'économies de mémoire importantes 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 ayant sa propre copie.

JavaScript n'est pas un langage de bas niveau. Il peut ne pas être très utile de penser au prototypage ou à d'autres modèles d'héritage comme un moyen de changer explicitement la façon dont la mémoire est allouée.

keparo
la source
49
@keparo: Vous vous trompez. Chaque objet a un objet prototype [interne] (qui peut l'être null), mais c'est très différent de la prototypepropriété - qui est sur les fonctions et sur laquelle le prototype de toutes les instances est défini lors de leur construction new. Je ne peux pas croire que cela ait vraiment eu 87 votes positifs :-(
Bergi
8
"The language is functional"êtes-vous sûr que c'est ce que signifie fonctionnel?
phant0m
23
J'appuie ce que @Bergi a dit sur les prototypes. Les fonctions ont une propriété prototype. Tous les objets, y compris les fonctions, ont une autre propriété interne accessible avec Object.getPrototypeOf (myObject) ou avec myObject .__ proto__ dans certains navigateurs. La propriété proto indique le parent de l'objet dans la chaîne du prototype (ou l'objet dont cet objet hérite). La propriété prototype (qui ne concerne que les fonctions) a indiqué l'objet qui deviendra le parent de tout objet utilisant la fonction pour créer de nouveaux objets à l'aide du nouveau mot-clé.
Jim Cooper
11
Cet article est assez erroné et confond la façon dont cela est défini. Travailler sur une réécriture.
RobG
37
Cette réponse est assez bizarre et semble manquer complètement le point de la question. La question semble être très courante sur la définition des propriétés de type au sein du constructeur par rapport au protoype, mais la moitié de la réponse concerne ce qui se passerait si vous l'utilisiez en Atant que fonction, et l'autre moitié concerne des façons obscures et peu orthodoxes de le faire. quelque chose de simple.
JLRishe
235

Comme d'autres l'ont dit dans la première version, l'utilisation de "this" donne à chaque instance de la classe A sa propre copie indépendante de la méthode de fonction "x". Alors que l'utilisation de "prototype" signifie que chaque instance de classe A utilisera la même copie de la méthode "x".

Voici un code pour montrer cette subtile différence:

// 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 diverses raisons de choisir l'une ou l'autre méthode. Mon échantillon est juste destiné à démontrer clairement la différence.

Benry
la source
5
C'est ce à quoi je m'attendrais, mais quand j'instancie un nouvel objet après avoir changé Ax comme ci-dessus, j'affiche toujours 'A' à moins que j'utilise A comme un singleton. jsbin.com/omida4/2/edit
jellyfishtree
19
C'est parce que mon exemple était faux. Cela ne fait mal que depuis deux ans. Soupir. Mais le point est toujours valable. J'ai mis à jour l'exemple avec un qui fonctionne réellement. Merci de l'avoir signalé.
Benry
4
C'est une méthode statique! : D
6
oui ... 'prototype' signifie un niveau statique ou de classe .. qui sera partagé par toutes les instances créées ... tandis que 'ceci' est une méthode d'instance dont chaque instance aura sa propre copie
Aneer Dev
7
Ce n'est pas statique. Statique, tel qu'il est utilisé dans la plupart des langages OO, implique qu'il n'y a pas de dépendance sur l' thisobjet, qui est le propriétaire de la méthode. c'est-à-dire que la méthode n'a pas d'objet qui en soit le propriétaire. Dans ce cas, il existe un thisobjet, comme indiqué dans la classe A dans l'exemple.
CJStuart
152

Prenez ces 2 exemples:

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

contre.

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 diffèrent sans expliquer POURQUOI. Je pense que c'est faux et si vous comprenez d'abord les fondamentaux, la différence deviendra évidente. Essayons d'abord d'expliquer les fondamentaux ...

a) Une fonction est un objet en JavaScript. CHAQUE objet en JavaScript obtient une propriété interne (ce qui signifie que vous ne pouvez pas y accéder comme les autres propriétés, sauf peut-être dans les navigateurs comme Chrome), souvent appelé __proto__(vous pouvez réellement taper anyObject.__proto__Chrome pour voir ce qu'il fait référence. C'est juste que , 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, à quoi __proto__pointe cette propriété? Eh bien, généralement un autre objet (nous expliquerons pourquoi plus tard). La seule façon de forcer JavaScript pour que la __proto__propriété ne pointe PAS vers un autre objet est d'utiliser var newObj = Object.create(null). Même si vous faites cela, la __proto__propriété existe TOUJOURS en tant que propriété de l'objet, juste elle ne pointe pas vers un autre objet, elle pointe vers null.

Voici où la plupart des gens se perdent:

Lorsque vous créez une nouvelle fonction en JavaScript (qui est également un objet, rappelez-vous?), Au moment où 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.prototypeest TOTALEMENT DIFFÉRENT de la __proto__propriété. Dans notre exemple, «A» a maintenant DEUX propriétés appelées «prototype» et __proto__. C'est une grande confusion pour les gens. prototypeet les __proto__propriétés ne sont aucunement liées, ce sont des choses distinctes pointant vers des valeurs distinctes.

Vous vous demandez peut-être: pourquoi JavaScript a-t-il une __proto__propriété créée sur chaque objet? Eh bien, un mot: délégation . Lorsque vous appelez une propriété sur un objet et que l'objet ne l'a pas, JavaScript recherche l'objet référencé par __proto__pour voir s'il l'a peut-être. S'il ne l'a pas, il regarde la __proto__propriété de cet objet et ainsi de suite ... jusqu'à la fin de la chaîne. D'où le nom de chaîne prototype . Bien sûr, si __proto__ne pointe pas vers un objet et pointe vers la place null, bonne chance, JavaScript s'en rend compte et vous reviendra undefinedpour la propriété.

Vous pouvez également vous demander pourquoi JavaScript crée une propriété appelée prototypepour une fonction lorsque vous définissez la fonction? Parce qu'il essaie de vous tromper, oui vous tromper qu'il fonctionne comme des langages basés 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 lorsque cette chose s'est produite. a1est une variable ordinaire à laquelle un nouvel objet vide a été affecté.

Le fait que vous ayez utilisé l'opérateur newavant un appel de fonction a A()fait ADDITIONNEL en arrière-plan. Le newmot-clé a créé un nouvel objet qui fait maintenant référence a1et cet objet est vide. Voici ce qui se passe en plus:

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

Nous sommes donc maintenant au point où nous avons un a1objet vide fraîchement cuit . Nous avons dit que tous les objets en JavaScript ont une __proto__propriété interne qui pointe vers quelque chose (l'a a1également), que ce soit nul ou un autre objet. Ce que fait l' newopérateur, c'est qu'il définit cette __proto__propriété pour qu'elle pointe vers la prototypepropriété de la fonction . Relisez ça. C'est fondamentalement ceci:

a1.__proto__ = A.prototype;

Nous avons dit que ce A.prototypen'est rien de plus qu'un objet vide (sauf si nous le changeons en quelque chose d'autre avant de le définir a1). Donc, maintenant, a1.__proto__pointe essentiellement vers la même chose A.prototype, qui est cet objet vide. Ils pointent tous les 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' var a1 = new A()instruction est traitée. Fondamentalement, A()est exécuté et si A est quelque chose comme ceci:

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

Toutes ces choses à l'intérieur function() { }vont s'exécuter. Lorsque vous atteignez la this.hey..ligne, thisest changé en a1et vous obtenez ceci:

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

Je ne couvrirai pas les raisons du thischangement, a1mais c'est une excellente réponse pour en savoir plus.

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

  1. Un tout nouvel objet vide est créé et affecté à a1.a1 = {}
  2. a1.__proto__la propriété est assignée pour pointer à la même chose que les A.prototypepoints vers (un autre objet vide {})

  3. La fonction A()est en cours d'exécution avec thisdéfini sur le nouvel objet vide créé à l'étape 1 (lisez la réponse que j'ai référencée ci-dessus pour savoir pourquoi thischange a1)

Maintenant, essayons de créer un autre objet:

var a2 = new A();

Les étapes 1, 2, 3 se répéteront. Avez-vous remarqué quelque chose? Le mot clé est répéter. Étape 1: a2sera un nouvel objet vide, étape 2: sa __proto__propriété pointera vers la même chose que A.prototypevers et surtout, étape 3: la fonction A()est à nouveau exécutée, ce qui signifie que la propriété contenant une fonction a2sera obtenue hey. a1et a2ont deux propriétés SEPARATE nommées heyqui pointent vers 2 fonctions SEPARATE! Nous avons maintenant des fonctions en double dans les mêmes deux objets différents faisant la même chose, oups ... Vous pouvez imaginer les implications en mémoire de cela si nous avons 1000 objets créés avec new A, après toutes les déclarations de fonctions prennent plus de mémoire que quelque chose comme le nombre 2. Donc comment éviter cela?

Rappelez-vous pourquoi la __proto__propriété existe sur chaque objet? Donc, si vous récupérez la yoManpropriété sur a1(qui n'existe pas), sa __proto__propriété sera consultée, qui si c'est un objet (et dans la plupart des cas c'est le cas), elle vérifiera si elle contient yoMan, et si ce n'est pas le cas, il consultera cet objet, __proto__etc. S'il le fait, 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 __proto__propriété pointe vers le même objet (vide) A.prototypepointe vers et faites ceci:

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

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

a1.hey

Il verra qu'il a1ne contient pas heyet il vérifiera son __proto__objet propriété pour voir s'il l'a, ce qui est le cas.

Avec cette approche, nous éliminons la partie de l'étape 3 où les fonctions sont dupliquées à chaque nouvelle création d'objet. Au lieu de a1et a2ayant une séparée heypropriété, maintenant aucun d'entre eux a. Je suppose que vous vous êtes déjà rendu compte. C'est la bonne chose ... si vous comprenez __proto__et Function.prototype, des questions comme celles-ci seront assez évidentes.

REMARQUE: certaines personnes ont tendance à ne pas appeler la propriété Prototype interne car __proto__, j'ai utilisé ce nom dans la publication pour le distinguer clairement de la Functional.prototypepropriété comme deux choses différentes.

daremkd
la source
1
Réponse vraiment approfondie et informative. J'ai fait quelques tests de mémoire en utilisant les structures d'objet ci-dessus (A.prototype.hey vs object this.hey) et créé 1000 instances de chacune. L'empreinte mémoire de l'approche de la propriété d'objet était d'environ 100 Ko plus grande que celle du prototype. J'ai ensuite ajouté une autre fonction dans le même but appelée "idiote" et elle a augmenté linéairement à 200 Ko. Pas significatif, mais pas d'arachides non plus.
jookyone
Ce qui est plus intéressant, c'est que la méthode prototype était légèrement plus lente que la méthode de propriété d'objet s'exécutant localement. Dans l'ensemble, je ne suis pas sûr que javascript doive être utilisé pour la manipulation de données d'objets dont la numérotation est supérieure à 10k, annulant ainsi toute raison de changer les approches en fonction des effets de mémoire potentiels. À ce stade, le travail doit être déchargé sur un serveur.
jookyone
Le point est __proto__et .prototypesont des choses totalement différentes.
Wayou
1
Je ne me sens pas satisfait de vous donner juste une note positive ... Bravo!
Kristianmitk
58

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

Une raison 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 affectée à this.x, mais pas en dehors de l'objet.

Matthew Crumley
la source
1
Voir cet article: stackoverflow.com/a/1441692/654708 pour un exemple sur la façon d'accéder aux membres privés via des prototypes.
GFoley83
@ GFoley83 cette réponse ne montre pas que - les méthodes prototypes ne peuvent accéder qu'aux propriétés "publiques" de l'objet donné. Seules les méthodes privilégiées (pas sur le prototype) peuvent accéder aux membres privés.
Alnitak
27

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

Glenn
la source
Les deux rendront la fonction xdisponible pour tous les objets dont le prototype se voit attribuer une nouvelle instance de A:function B () {}; B.prototype = new A(); var b = new B(); b.x() // Will call A.x if A is defined by first example;
Spencer Williams
21

Le problème ultime avec l'utilisation thisau lieu de prototypeest que lors de la substitution d' une méthode, le constructeur de la classe de base fera toujours référence à la méthode substitué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 avez suffisamment d'expérience pour connaître une fuite lorsque vous en voyez une. De plus, avoir à mettre la logique du constructeur après les définitions de méthode n'est pas 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
tarkabak
la source
20

Chaque objet est lié à un objet prototype. Lorsque vous essayez d'accéder à une propriété qui n'existe pas, JavaScript recherchera dans l'objet prototype de l'objet cette propriété et la renverra si elle existe.

La prototypepropriété 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 new.


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

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 vers laquelle toutes les instances créées avec Apointent.

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, une copie de la fonction est affectée à chaque instance . Dans le deuxième exemple, une seule copie de la fonction est partagée par toutes les instances .

pishpish
la source
1
A voté ceci pour être la réponse la plus directe à la question.
Nick Pineda
1
J'ai aimé votre approche directe !! cogne!
Prince Vijay Pratap
16

Quelle est la différence? => Beaucoup.

Je pense que la thisversion est utilisée pour permettre l'encapsulation, c'est-à-dire le masquage des données. Il aide à manipuler des variables privées.

Voyons 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 prototypestructure peut être appliquée comme suit:

Différents adultes ont des âges différents, mais tous les adultes ont les mêmes droits.
Donc, nous l'ajoutons en utilisant un prototype, plutôt que cela.

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

Regardons maintenant l'implémentation.

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.

oozzal
la source
3
+1 Une réponse beaucoup moins compliquée et plus graphique que les autres. Mais vous devriez élaborer un peu plus avant de fournir ces (bons) exemples.
yerforkferchips
1
Je ne sais pas "cette version est utilisée pour permettre l'encapsulation, c'est-à-dire le masquage des données". Si une propriété à l'intérieur d'une fonction est définie en utilisant "this" comme dans "this.myProperty = ...", une telle propriété n'est pas "privée" et est accessible à partir d'objets en dehors de la classe en utilisant "new".
NoChance
14

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.

harropriiz
la source
14

Je sais que cela a été répondu à mort, mais je voudrais montrer un exemple réel de différences de vitesse.

Fonctionne directement sur l'objet

Fonction sur prototype

Ici, nous créons 2000000 de nouveaux objets avec une printméthode dans Chrome. Nous stockons chaque objet dans un tableau. Mettre printle prototype prend environ la moitié du temps.

Arnav Aggarwal
la source
13

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

La plupart des réponses mentionnaient déjà la différence, c'est-à-dire lorsque le prototypage de la fonction est partagé avec toutes les (futures) instances. Alors que déclarer la fonction dans la classe créera une copie pour chaque instance.

En général, il n'y a pas de bien ou de mal, c'est plus une question de goût ou une décision de conception en fonction de vos besoins. Le prototype est cependant la technique qui est utilisée pour se développer de manière orientée objet, comme j'espère que vous le verrez à la fin de cette réponse.

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

Modèle de décorateur d'objet

Je ne sais pas si ce modèle est toujours pertinent de nos jours, mais il existe. Et c'est bon de le savoir. 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 invoquée, une fonction peut stocker des propriétés comme tout autre objet.

Dans ce cas, il Cars'agit d'une fonction ( pensez également objet ) qui peut être invoquée comme vous en avez l'habitude. Il a une propriété methods(qui est un objet avec une movefonction). Quand Carest invoqué, la extendfonction est appelée, ce qui fait de la magie et étend la Carfonction (pense objet) avec les 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 une discussion sur l'utilisation de techniques pour définir des méthodes partagées ou sur l'utilisation de méthodes définies en ligne dans le corps du constructeur. Dans les deux cas, chaque instance a sa propre movefonction.

Le motif prototypique ne se prête pas bien au même examen, car le partage de fonctions via une délégation de prototype est l'objectif même du motif prototypique. Comme d'autres l'ont souligné, il devrait avoir une meilleure empreinte mémoire.

Cependant, il y a un point intéressant à savoir: chaque prototypeobjet a une propriété de commodité constructor, qui renvoie à la fonction (pensez à l'objet) à laquelle il était attaché.

Concernant les trois dernières lignes:

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

amy.constructorLa recherche échoue et est donc déléguée à Car.prototype, qui a la propriété constructeur. Et il en amy.constructorest ainsi Car.

En outre, amyest un instanceof Car. L' instanceofopérateur fonctionne en voyant si l'objet prototype de l'opérande droit ( Car) peut être trouvé n'importe où dans la amychaîne prototype ( ) de l'opérande gauche .

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 l'exemple ci-dessous:

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

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

L' instanceofopérateur revient false, car Dogle prototype de ne se trouve nulle part dans fidola chaîne de prototypes de. fidoest un objet simple qui est créé avec un objet littéral, c'est-à-dire qu'il délègue simplement à Object.prototype.

Modèles pseudoclassiques

C'est vraiment juste une autre forme du modèle prototypique sous forme simplifiée et plus familière pour ceux qui programment en Java par exemple, car il utilise le newconstructeur.

Cela fait la même chose que dans le modèle prototypique, c'est juste du sucre syntaxique au-dessus du modèle prototypique.

Cependant, la principale différence est qu'il existe des optimisations implémentées dans les moteurs JavaScript qui ne s'appliquent que lors de l'utilisation du modèle pseudoclassique. Pensez au modèle pseudoclassique, une version probablement plus rapide du modèle prototypique; 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 réaliser comment la 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 ( locvariable dans les exemples).

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

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

Ely
la source
Pas pour frapper un article très complet, mais je pensais que OO et l'héritage prototypique étaient essentiellement des écoles de pensées différentes.
Nick Pineda
Ils le sont, mais on peut "faire OO" avec différentes techniques / pensées, n'est-ce pas?
Ely
Je ne sais pas vraiment. Beaucoup disent simplement que la philosophie prototypique est juste différente et beaucoup essaient de la comparer à OO parce que c'est l'école de pensée à laquelle beaucoup sont habitués.
Nick Pineda
Je veux dire, si vous voulez pratiquer le style OO et que le langage propose un ensemble de techniques qui vous aident à le faire, ce n'est pas nécessairement faux.
Ely
11

Je pense que @Matthew Crumley a raison. Ils sont fonctionnellement , sinon structurellement, équivalents. Si vous utilisez Firebug pour regarder les objets créés à l'aide new, vous pouvez voir qu'ils sont identiques. Cependant, ma préférence serait la suivante. Je suppose 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 juste d'illustrer comment je définissais mes classes en javascript. Le nom de la variable a été modifié pour refléter cela.

tvanfosson
la source
2
_instance_var comme dans la propriété initializeand x methods do not refer to the _instance_var` sur une Ainstance, mais sur une propriété globale. À utiliser this._instance_varsi vous vouliez utiliser la _instance_varpropriété d'une Ainstance.
Lekensteyn
2
Le plus drôle, c'est que Benry a également fait une telle erreur, qui a également été découverte après deux ans: p
Lekensteyn
10

Comme discuté dans d'autres réponses, c'est vraiment une considération de performance car la fonction dans le prototype est partagée avec toutes les instanciations - plutôt que la fonction créée pour chaque instanciation.

J'ai mis en place un jsperf pour le montrer. Il y a une différence dramatique dans le temps qu'il faut pour instancier la classe, bien qu'elle ne soit vraiment pertinente que si vous faites de nombreuses instances.

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

Devgr
la source
8

Pensez au langage typé statiquement, les choses sur prototypesont statiques et les choses sur thissont liées à l'instance.

Wayou
la source