Question Utilisation de 'prototype' contre 'ceci' 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
};

719
2017-11-22 04:39


origine


Réponses:


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

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

  • Un constructeur prototype fournit un moyen de partager des méthodes et des valeurs entre instances via le privé de l'instance [[Prototype]] propriété.
  • Une fonction de ce est défini par la façon dont la fonction est appelée ou par l'utilisation de lier (pas discuté ici). Lorsqu'une fonction est appelée sur un objet (par ex. myObj.method()) puis ce dans la méthode référence l'objet. Où ce n'est pas fixé par l'appel ou par l'utilisation de lier, il est par défaut à 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 tout est un objet, y compris des fonctions.

Voici donc les extraits en question:

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

Dans ce cas, variable A est affecté une valeur qui est une référence à une fonction. Lorsque cette fonction est appelée en utilisant A(), les fonctions ce n'est pas défini par l'appel de sorte qu'il est par défaut à l'objet global et l'expression this.x est effectivement window.x. Le résultat est qu'une référence à l'expression de la 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, variable A est attribué une référence à une fonction. En JavaScript, tous les objets de fonctions ont un prototype propriété par défaut, donc il n'y a pas de code séparé pour créer un A.prototype objet.

Dans la deuxième ligne, A.prototype.x est attribué une référence à une fonction. Cela va créer un X propriété si elle n'existe pas, ou attribuez une nouvelle valeur si c'est le cas. Donc, la différence avec le premier exemple est quel objet X la propriété est impliquée dans l'expression.

Un autre exemple est ci-dessous. C'est similaire au premier (et peut-être ce que vous vouliez dire à propos de):

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

Dans cet exemple, le new Un opérateur a été ajouté avant l'expression de la fonction afin que la fonction soit appelée en tant que constructeur. Quand appelé avec new, les fonctions ce est défini pour référencer un nouvel objet dont le [[Prototype]] propriété est définie pour référencer le public du constructeur prototype. Donc, dans la déclaration d'affectation, le x La propriété sera créée sur ce nouvel objet. Lorsqu'il est appelé en tant que constructeur, une fonction renvoie son ce objet par défaut, il n'y a donc pas besoin d'un séparé return this; déclaration.

Pour vérifier cela UNE a un X propriété:

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

Ceci est une utilisation rare de Nouveau, puisque la seule façon de référencer le constructeur est via Un.constructeur. Il serait beaucoup plus commun de faire:

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

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

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

Dans ce cas, A affecté la valeur de retour de l'appel de la fonction sur le côté droit. Ici encore, depuis cen'est pas défini dans l'appel, il référencera l'objet global et this.x est effectivement window.x. Puisque la fonction ne retourne rien, A aura une valeur de undefined.

Ces différences entre les deux approches se manifestent également si vous numérotez 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 se peut qu'il n'y ait pas d'économies significatives de mémoire entre les deux approches, cependant l'utilisation du prototype pour partager les méthodes et les 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 n'est peut-être pas très utile de penser au prototypage ou à d'autres modèles d'héritage pour modifier explicitement la façon dont la mémoire est allouée.


434
2017-11-22 05:41



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 fonction "x". Alors que l'utilisation de "prototype" signifiera que chaque instance de la classe A utilisera la même copie de la méthode "x".

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


223
2018-01-22 13:47



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 comment ils sont différents sans expliquer pourquoi. Je pense que c'est faux et si vous comprenez les fondamentaux en premier, la différence deviendra évidente. Essayons d'expliquer les fondamentaux en premier ...

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és __proto__ (Vous pouvez réellement taper anyObject.__proto__ dans Chrome pour voir ce qu'il référence. C'est juste cela, une propriété, rien de plus. Une propriété dans JavaScript = une variable à l'intérieur d'un objet, rien de plus. Que font les variables? Ils pointent vers des choses.

Alors qu'est-ce que c'est __proto__ points de propriété à? Eh bien, habituellement un autre objet (nous expliquerons pourquoi plus tard). La seule façon de forcer JavaScript pour le __proto__ propriété de ne pas pointer vers un autre objet est d'utiliser var newObj = Object.create(null). Même si vous faites cela, le __proto__ La propriété existe toujours en tant que propriété de l'objet, mais elle ne pointe pas vers un autre objet, elle indique null.

Voici où la plupart des gens se confondent:

Lorsque vous créez une nouvelle fonction en JavaScript (qui est aussi un objet, souvenez-vous?), Au moment où il est défini, JavaScript crée automatiquement une nouvelle propriété appelée prototype. Essayez-le:

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

A.prototypeest TOTALEMENT DIFFERENT 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. prototype et __proto__ les propriétés ne sont en aucun cas liées, ce sont des choses séparées pointant vers des valeurs séparées.

Vous vous demandez peut-être pourquoi JavaScript a __proto__ propriété créée sur chaque objet unique? 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 si ça l'a peut-être. Si ce n'est pas le cas, alors il regarde cet objet __proto__ propriété 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 vers un objet et pointe à la place null, bien malchance, JavaScript s'en rend compte et vous reviendra undefined pour la propriété.

Vous pouvez également vous demander 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 cela fonctionne comme des langages basés sur des classes.

Continuons avec notre exemple et créer un "objet" sur A:

var a1 = new A();

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

Le fait que vous avez utilisé l'opérateur new avant une invocation de fonction A() fait quelque chose d'additionnel en arrière-plan. le new mot-clé créé un nouvel objet qui référence maintenant a1 et cet objet est vide. Voici ce qui se passe en plus:

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

Nous sommes maintenant au point où nous avons un vide fraîchement cuit a1 objet. Nous avons dit que tous les objets en JavaScript ont un interne __proto__ propriété qui pointe vers quelque chose (a1 l'a aussi), que ce soit nul ou un autre objet. Qu'est-ce que new l'opérateur fait est qu'il définit que __proto__ propriété pour pointer vers la fonction prototype propriété. Lisez ça à nouveau. C'est essentiellement ceci:

a1.__proto__ = A.prototype;

Nous avons dit que A.prototype n'est rien de plus qu'un objet vide (sauf si nous le changeons en quelque chose d'autre avant de définir a1). Alors maintenant, fondamentalement a1.__proto__ pointe vers la même chose A.prototype pointe vers, qui est cet objet vide. Ils pointent tous les deux vers le même objet qui a été créé quand cette ligne s'est passée:

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

Maintenant, il y a une autre chose qui se passe quand var a1 = new A() déclaration 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') } };

Tous ces trucs à l'intérieur function() { } va exécuter. Lorsque vous atteignez le this.hey.. ligne, this est changé en a1 et vous obtenez ceci:

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

Je ne vais pas couvrir pourquoi this changements à a1 mais C'est une excellente réponse pour apprendre plus.

Donc, pour résumer, quand vous faites `` var a1 = new A () `il y a 3 choses qui se passent en arrière-plan:

  1. Un objet vide totalement nouveau est créé et affecté à a1. a1 = {}
  2. a1.__proto__ la propriété est assignée à pointer à la même chose que A.prototype pointe vers (un autre objet vide {})

  3. La fonction A() est en cours d'exécution avec this mettre à la nouvelle, objet vide créé à l'étape 1 (lire la réponse que j'ai mentionné ci-dessus pour savoir pourquoi this changements à a1)

Essayons maintenant 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: a2 sera un nouvel objet vide, étape 2: son __proto__ la propriété pointera vers la même chose A.prototype points à et le plus important, étape 3: fonction A() est de nouveau exécuté, ce qui signifie que a2 aura hey propriété contenant une fonction. a1 et a2 avoir deux propriétés SEPARATE nommées hey qui pointent vers 2 fonctions SÉPARÉES! Nous avons maintenant des fonctions dupliquées dans deux objets différents faisant la même chose, oups ... Vous pouvez imaginer les implications de la mémoire 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 numéro 2. Alors, comment pouvons-nous empêcher cela?

Rappelez-vous pourquoi le __proto__ la propriété existe sur chaque objet? Alors que si vous récupérez le yoMan propriété sur a1 (qui n'existe pas), son __proto__ la propriété sera consultée, et si c'est un objet (et c'est le cas dans la plupart des cas), elle vérifiera si elle contient yoManet si ce n'est pas le cas, il consultera cet objet __proto__ etc. Si c'est le cas, il prendra cette valeur de propriété et vous l'affichera.

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

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

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

a1.hey

Il verra que a1 ne contient pas hey et il va vérifier son __proto__ objet de propriété pour voir si elle 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 a1 et a2 avoir un séparé hey propriété, maintenant aucun d'entre eux l'a. Lequel, je suppose, vous vous êtes compris maintenant. 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 __proto__, J'ai utilisé ce nom par la poste pour le distinguer clairement Functional.prototype propriété comme deux choses différentes.


118
2017-11-22 05:03



Dans la plupart des cas, ils sont essentiellement les mêmes, mais la deuxième version enregistre 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 pour 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.


54
2017-11-22 04:43



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


25
2017-09-21 11:15



Le problème ultime avec l'utilisation this au lieu de prototype est que lors de la substitution d'une méthode, le constructeur de la classe de base se référera toujours à 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 savoir une fuite quand vous en voyez une. De même, le fait de devoir mettre la logique du constructeur après les définitions de la 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

19
2018-02-11 15:30



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

le prototype propriété d'un constructeur de fonction se réfère à 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 le A fonction.

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 que toutes les instances créées avec A pointer vers.

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.


17
2018-01-06 06:13



Quelle est la différence? => Beaucoup.

Je pense que le this la version est utilisée pour activer l'encapsulation, c'est-à-dire le masquage de données. Cela aide à manipuler les 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 le prototype la structure 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 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.


14
2017-11-22 06:16