web-dev-qa-db-fra.com

Comment fonctionne JavaScript .prototype?

Je ne suis pas dans des langages de programmation dynamiques, mais j'ai écrit ma juste part de code JavaScript. Je n'ai jamais vraiment compris cette programmation basée sur des prototypes. Quelqu'un sait-il comment cela fonctionne? 

var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Je me souviens de beaucoup de discussions que j'avais eues avec des gens il y a quelque temps (je ne suis pas tout à fait sûr de ce que je fais), mais si je comprends bien, il n'y a pas de concept de classe. C'est juste un objet, et les instances de ces objets sont des clones de l'original, non?

Mais quel est le but exact de cette propriété ".prototype" en JavaScript? Quel est le lien avec l'instanciation d'objets?

Mise à jour: manière correcte

var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!

function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK

Aussi ces diapositives vraiment aidé beaucoup.

1918
John Leidegren

Chaque objet JavaScript a une propriété interne appelée [[Prototype]] . Si vous recherchez une propriété via obj.propName ou obj['propName'] et que l'objet ne possède pas une telle propriété (qui peut être vérifiée via obj.hasOwnProperty('propName')), le moteur d'exécution recherche la propriété dans l'objet référencé par [[Prototype]]. Si l'objet prototype n'a pas non plus une telle propriété, son prototype est vérifié à son tour, entraînant ainsi la chaîne prototype-chain de l'objet d'origine jusqu'à ce qu'une correspondance soit trouvée ou que sa fin soit atteinte.

Certaines implémentations JavaScript permettent un accès direct à la propriété [[Prototype]], par exemple via une propriété non standard nommée __proto__. En général, il est uniquement possible de définir le prototype d'un objet lors de la création de l'objet: Si vous créez un nouvel objet via new Func(), la propriété [[Prototype]] de l'objet sera définie sur l'objet référencé par Func.prototype.

Cela permet de simuler des classes en JavaScript, bien que le système d'héritage de JavaScript soit - comme nous l'avons vu - prototypique et non basé sur les classes:

Imaginez simplement les fonctions de constructeur en tant que classes et les propriétés du prototype (c'est-à-dire de l'objet référencé par la propriété prototype de la fonction de constructeur) comme des membres partagés, c'est-à-dire des membres identiques pour chaque instance. Dans les systèmes basés sur des classes, les méthodes sont implémentées de la même manière pour chaque instance. Les méthodes sont donc normalement ajoutées au prototype, tandis que les champs d'un objet sont spécifiques à une instance et donc ajoutés à l'objet lui-même lors de la construction.

958
Christoph

Dans un langage implémentant l'héritage classique tel que Java, C # ou C++, vous commencez par créer une classe - un modèle pour vos objets - puis vous pouvez créer de nouveaux objets à partir de cette classe ou vous pouvez étendre la classe en définissant une nouvelle classe qui augmente la classe d'origine.

En JavaScript, vous créez d'abord un objet (il n'y a pas de concept de classe), vous pouvez ensuite augmenter votre propre objet ou en créer de nouveaux. Ce n'est pas difficile, mais un peu étranger et difficile à métaboliser pour quelqu'un habitué à la manière classique.

Exemple:

//Define a functional object to hold persons in JavaScript
var Person = function(name) {
  this.name = name;
};

//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
  return this.name;
};

//Create a new object of type Person
var john = new Person("John");

//Try the getter
alert(john.getName());

//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
  alert('Hello, my name is ' + this.getName());
};

//Call the new method on john
john.sayMyName();

Jusqu'à présent, j'ai étendu l'objet de base, maintenant je crée un autre objet, puis j'hérite de Person.

//Create a new object of type Customer by defining its constructor. It's not 
//related to Person for now.
var Customer = function(name) {
    this.name = name;
};

//Now I link the objects and to do so, we link the prototype of Customer to 
//a new instance of Person. The prototype is the base that will be used to 
//construct all new instances and also, will modify dynamically all already 
//constructed objects because in JavaScript objects retain a pointer to the 
//prototype
Customer.prototype = new Person();     

//Now I can call the methods of Person on the Customer, let's try, first 
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();

//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
    return this.amountDue;
};

//Let's try:       
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

var Person = function (name) {
    this.name = name;
};
Person.prototype.getName = function () {
    return this.name;
};
var john = new Person("John");
alert(john.getName());
Person.prototype.sayMyName = function () {
    alert('Hello, my name is ' + this.getName());
};
john.sayMyName();
var Customer = function (name) {
    this.name = name;
};
Customer.prototype = new Person();

var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();
Customer.prototype.setAmountDue = function (amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function () {
    return this.amountDue;
};
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

Bien que, comme je l'ai dit, je ne puisse pas appeler setAmountDue (), getAmountDue () sur une personne.

//The following statement generates an error.
john.setAmountDue(1000);
1774
stivlo

Je joue un rôle en tant qu'enseignant JavaScript et le concept de prototype a toujours été un sujet controversé à couvrir lorsque j'enseigne. Il m'a fallu un certain temps pour trouver une bonne méthode pour clarifier le concept, et maintenant, dans ce texte, je vais essayer d'expliquer comment fonctionne JavaScript .prototype.


Il s'agit d'un modèle d'objet basé sur un prototype très simple qui serait considéré comme un exemple lors de l'explication, sans commentaire pour le moment:

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    console.log(this.name);
}
var person = new Person("George");

Nous devons tenir compte de certains points cruciaux avant de passer au concept de prototype.

1- Fonctionnement effectif des fonctions JavaScript:

Pour faire le premier pas, nous devons comprendre le fonctionnement réel des fonctions JavaScript, en tant que classe utilisant une fonction utilisant le mot clé this ou juste une fonction normale avec ses arguments, ce qu’elle fait et ce qu’elle renvoie. .

Supposons que nous voulions créer un modèle d'objet Person. mais dans cette étape, je vais essayer de faire exactement la même chose sans utiliser les mots clés prototype et new.

Donc, à cette étape functions _, objects et this mot-clé sont tout ce que nous avons.

La première question serait comment le mot clé this pourrait être utile sans utiliser le mot clé new.

Donc, pour répondre à cela, disons que nous avons un objet vide et deux fonctions comme:

var person = {};
function Person(name){  this.name = name;  }

function getName(){
    console.log(this.name);
}

et maintenant sans utiliser le mot clé new comment nous pourrions utiliser ces fonctions. Donc, JavaScript a 3 façons différentes de le faire:

une. La première méthode consiste simplement à appeler la fonction en tant que fonction normale:

Person("George");
getName();//would print the "George" in the console

dans ce cas, il s'agirait de l'objet contextuel en cours, qui est généralement l'objet global window dans le navigateur ou GLOBAL dans Node.js. Cela signifie que nous aurions window.name dans le navigateur ou GLOBAL.name dans Node.js, avec "George" comme valeur.

b. On peut les {attacher} _ à un objet, ainsi que ses propriétés

-Le moyen le plus simple pour ce faire est de modifier l'objet person vide, comme ceci:

person.Person = Person;
person.getName = getName;

de cette façon, nous pouvons les appeler comme:

person.Person("George");
person.getName();// -->"George"

et maintenant l'objet person est comme:

Object {Person: function, getName: function, name: "George"}

-L'autre façon d'attacher une propriété à un objet utilise la variable prototype de cet objet qui peut être trouvée dans n'importe quel objet JavaScript portant le nom __proto__, et j'ai essayé de l'expliquer un peu dans le résumé. partie. Nous pourrions donc obtenir le même résultat en faisant:

person.__proto__.Person = Person;
person.__proto__.getName = getName;

Mais Ainsi, nous modifions le Object.prototype, car chaque fois que nous créons un objet JavaScript à l'aide de littéraux ({ ... }), il est créé sur la base de Object.prototype, ce qui signifie qu'il est attaché au nouvel objet créé. un attribut nommé __proto__, donc si nous le modifions, comme nous l'avons fait dans notre précédent extrait de code, tous les objets JavaScript seraient modifiés, ce qui ne serait pas une bonne pratique. Alors, quelle pourrait être la meilleure pratique maintenant:

person.__proto__ = {
    Person: Person,
    getName: getName
};

et maintenant les autres objets sont en paix, mais cela ne semble toujours pas être une bonne pratique. Nous avons donc encore une solution, mais pour utiliser cette solution, nous devrions revenir à cette ligne de code où l’objet person a été créé (var person = {};), puis le changer comme suit:

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

il crée une nouvelle variable Object et associe la variable propertiesObject à l'attribut __proto__. Donc, pour vous assurer que vous pouvez faire:

console.log(person.__proto__===propertiesObject); //true

Mais le point délicat ici est que vous avez accès à toutes les propriétés définies dans __proto__ au premier niveau de l’objet person (lisez la partie récapitulative pour plus de détails).


comme vous le voyez, utiliser l'une de ces deux méthodes this indiquerait exactement l'objet person.

c. JavaScript a un autre moyen de fournir à la fonction this, qui utilise call ou apply pour appeler la fonction.

La méthode apply () appelle une fonction avec cette donnée et arguments fournis sous forme de tableau (ou d'objet semblable à un tableau).

et

La méthode call () appelle une fonction avec cette donnée et arguments fournis individuellement.

de cette façon, qui est ma préférée, nous pouvons facilement appeler nos fonctions comme:

Person.call(person, "George");

ou

//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);

getName.call(person);   
getName.apply(person);

ces 3 méthodes sont les étapes initiales importantes pour comprendre la fonctionnalité .prototype.


2- Comment fonctionne le mot clé new?

c’est la deuxième étape pour comprendre la fonctionnalité .prototype. C’est ce que j’utilise pour simuler le processus:

function Person(name){  this.name = name;  }
my_person_prototype = { getName: function(){ console.log(this.name); } };

dans cette partie, je vais essayer de suivre toutes les étapes de JavaScript, sans utiliser les mots clés new et prototype, lorsque vous utilisez le mot clé new. alors quand nous faisons new Person("George"), la fonction Person sert de constructeur, voici ce que fait JavaScript, un par un:

une. Tout d'abord, cela crée un objet vide, en gros un hash vide comme:

var newObject = {};

b. la prochaine étape de JavaScript consiste à attacher tous les objets prototypes de l'objet nouvellement créé.

nous avons my_person_prototype similaire à l'objet prototype.

for(var key in my_person_prototype){
    newObject[key] = my_person_prototype[key];
}

Ce n’est pas ainsi que JavaScript associe les propriétés définies dans le prototype. La méthode actuelle est liée au concept de chaîne prototype.une. & b. Au lieu de ces deux étapes, vous pouvez obtenir exactement le même résultat en faisant:.


var newObject = Object.create(my_person_prototype); //here you can check out the __proto__ attribute console.log(newObject.__proto__ === my_person_prototype); //true //and also check if you have access to your desired properties console.log(typeof newObject.getName);//"function"

nous pouvons maintenant appeler la fonction getName dans notre my_person_prototype:

newObject.getName();

c. alors il donne cet objet au constructeur,

Person.call(newObject, "George");

ou

Person.apply(newObject, ["George"]);

alors le constructeur peut faire ce qu'il veut, parce que this à l'intérieur de ce constructeur est l'objet qui vient d'être créé.

maintenant le résultat final avant de simuler les autres étapes: 
 Objet {nom: "George"}

.

résumé:


En gros, lorsque vous utilisez le mot clé nouveau sur une fonction, vous appelez cette fonction et cette fonction sert de constructeur. Par conséquent, lorsque vous dites:

new FunctionName()

console.log(Object.prototype.__proto__===null);//true .

Et c'est ainsi que fonctionne l'héritage en JavaScript.

.prototype and also it is possible that this object has its own internal prototype. and so on.

170
Mehran Hatami

prototype vous permet de faire des cours. Si vous n'utilisez pas prototype, il devient statique.

Voici un court exemple.

var obj = new Object();
obj.test = function() { alert('Hello?'); };

Dans le cas ci-dessus, vous avez le test d'appel de funcation statique. Cette fonction est accessible uniquement par obj.test où vous pouvez imaginer que obj est une classe.

où comme dans le code ci-dessous

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

L'obj est devenu une classe qui peut maintenant être instanciée. Plusieurs instances d'obj peuvent exister et elles ont toutes la fonction test.

Ce qui précède est ma compréhension. J'en fais un wiki de la communauté, afin que les gens puissent me corriger si je me trompe.

73
Ramesh

Les sept Koans du prototype

Alors que Ciro San descendait du mont Fire Fox après une profonde méditation, son esprit était clair et paisible.

Cependant, sa main était agitée, et par elle-même attrapa un pinceau et nota les notes suivantes.


0) Deux choses différentes peuvent être appelées "prototype":

  • la propriété prototype, comme dans obj.prototype

  • la propriété interne du prototype, notée [[Prototype]]dans ES5 .

    Il peut être récupéré via l'ES5 Object.getPrototypeOf().

    Firefox le rend accessible via la propriété __proto__ en tant qu'extension. ES6 mentionne maintenant } _ quelques exigences facultatives pour __proto__.


1) Ces concepts existent pour répondre à la question:

Quand je fais obj.property, où JS cherche-t-il .property?

Intuitivement, l'héritage classique devrait affecter la recherche de propriété.


2)

  • __proto__ est utilisé pour la recherche de propriété point . comme dans obj.property
  • .prototype est pas utilisé pour la recherche directement, seulement indirectement car il détermine __proto__ à la création de l'objet avec new.

L'ordre de recherche est:

  • obj propriétés ajoutées avec obj.p = ... ou Object.defineProperty(obj, ...)
  • propriétés de obj.__proto__
  • propriétés de obj.__proto__.__proto__, etc.
  • si un __proto__ est null, retournez undefined.

C'est ce que l'on appelle la chaîne prototype.

Vous pouvez éviter la recherche . avec obj.hasOwnProperty('key') et Object.getOwnPropertyNames(f)


3) Il existe deux méthodes principales pour définir obj.__proto__:

  • new:

    var F = function() {}
    var f = new F()
    

    alors new a défini:

    f.__proto__ === F.prototype
    

    Ce est l'endroit où .prototype est utilisé.

  • Object.create:

     f = Object.create(proto)
    

    ensembles:

    f.__proto__ === proto
    

4) Le code:

var F = function() {}
var f = new F()

Correspond au schéma suivant:

(Function)       (  F  )                                      (f)
 |  ^             | | ^                                        |
 |  |             | | |                                        |
 |  |             | | +-------------------------+              |
 |  |constructor  | |                           |              |
 |  |             | +--------------+            |              |
 |  |             |                |            |              |
 |  |             |                |            |              |
 |[[Prototype]]   |[[Prototype]]   |prototype   |constructor   |[[Prototype]]
 |  |             |                |            |              |
 |  |             |                |            |              |
 |  |             |                | +----------+              |
 |  |             |                | |                         |
 |  |             |                | | +-----------------------+
 |  |             |                | | |
 v  |             v                v | v
(Function.prototype)              (F.prototype)
 |                                 |
 |                                 |
 |[[Prototype]]                    |[[Prototype]]
 |                                 |
 |                                 |
 | +-------------------------------+
 | |
 v v
(Object.prototype)
 | | ^
 | | |
 | | +---------------------------+
 | |                             |
 | +--------------+              |
 |                |              |
 |                |              |
 |[[Prototype]]   |constructor   |prototype
 |                |              |
 |                |              |
 |                | -------------+
 |                | |
 v                v |
(null)           (Object)

Ce diagramme présente de nombreux nœuds d’objet prédéfinis en langage: null, Object, Object.prototype, Function et Function.prototype. Nos 2 lignes de code ont uniquement créé f, F et F.prototype.


5).constructor vient normalement de F.prototype à la recherche .:

f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor

Lorsque nous écrivons f.constructor, JavaScript effectue la recherche . en tant que:

  • f n'a pas .constructor
  • f.__proto__ === F.prototype a .constructor === F, alors prenez-le

Le résultat f.constructor == F est intuitivement correct, puisque F est utilisé pour construire f, par ex. définir des champs, un peu comme dans les langues classiques OOP. 


6) La syntaxe d'héritage classique peut être obtenue en manipulant des chaînes de prototypes.

ES6 ajoute les mots-clés class et extends, qui sont simplement du sucre syntaxique pour une folie de manipulation de prototype précédemment possible.

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// http://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined

Diagramme simplifié sans tous les objets prédéfinis:

      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype

Après avoir lu ce fil de discussion, je suis confus avec la chaîne de prototypes JavaScript, puis j'ai trouvé ces graphiques. 

http://iwiki.readthedocs.org/fr/latest/javascript/js_core.html#inheritance*[[protytype]]* and <code>prototype</code> property of function objects

c'est un graphique clair montrant l'héritage JavaScript par chaîne de prototypes

et 

http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/

celui-ci contient un exemple avec du code et plusieurs diagrammes de Nice.

la chaîne de prototypes revient finalement à Object.prototype. 

la chaîne de prototypes peut être techniquement étendue aussi longtemps que vous le souhaitez, en définissant le prototype de la sous-classe sur un objet de la classe parente.

J'espère que cela vous aidera également à comprendre la chaîne de prototypes JavaScript.

65
rockXrock

Chaque objet a une propriété interne, [[Prototype]], le liant à un autre objet:

object [[Prototype]] -> anotherObject

En javascript traditionnel, l’objet lié est la propriété prototype d’une fonction:

object [[Prototype]] -> aFunction.prototype

Certains environnements exposent [[Prototype]] comme __proto__:

anObject.__proto__ === anotherObject

Vous créez le lien [[Prototype]] lors de la création d'un objet.

// (1) Object.create:
var object = Object.create(anotherObject)
// object.__proto__ = anotherObject

// (2) ES6 object initializer:
var object = { __proto__: anotherObject };
// object.__proto__ = anotherObject

// (3) Traditional JavaScript:
var object = new aFunction;
// object.__proto__ = aFunction.prototype

Donc, ces déclarations sont équivalentes:

var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;

Une instruction new ne montre pas le lien cible (Object.prototype) lui-même; à la place, la cible est impliquée par le constructeur (Object).

Rappelles toi:

  • Chaque objet a un lien, [[Prototype]], parfois exposé en tant que __proto__.
  • Chaque fonction a une propriété prototype.
  • Les objets créés avec new sont liés à la propriété prototype de leur constructeur.
  • Si une fonction n'est jamais utilisée en tant que constructeur, sa propriété prototype sera inutilisée.
  • Si vous n'avez pas besoin d'un constructeur, utilisez Object.create au lieu de new.
37
sam

Javascript n'a pas d'héritage au sens habituel, mais il a la chaîne de prototypes.

chaîne de prototype

Si un membre d'un objet ne peut pas être trouvé dans l'objet, il le cherche dans la chaîne de prototypes. La chaîne est composée d'autres objets. Le prototype d'une instance donnée est accessible avec la variable __proto__. Chaque objet en a un, car il n'y a pas de différence entre les classes et les instances en javascript.

L’avantage d’ajouter une fonction/variable au prototype est qu’elle ne doit figurer dans la mémoire qu’une seule fois, pas pour chaque instance.

C'est également utile pour l'héritage, car la chaîne de prototypes peut contenir de nombreux autres objets.

25
Georg Schölly

quel est le but exact de cette propriété ".prototype"?

L'interface avec les classes standard devient extensible. Par exemple, vous utilisez la classe Array et vous devez également ajouter un sérialiseur personnalisé pour tous vos objets tableau. Voulez-vous passer du temps à coder une sous-classe, ou utiliser une composition ou ... La propriété prototype résout ce problème en permettant aux utilisateurs de contrôler l'ensemble exact de membres/méthodes disponibles pour une classe.

Pensez aux prototypes comme un pointeur vtable supplémentaire. Lorsque certains membres sont absents de la classe d'origine, le prototype est recherché à l'exécution.

20
dirkgently

Il peut être utile de classer les chaînes de prototypes en deux catégories.

Considérons le constructeur:

 function Person() {}

La valeur de Object.getPrototypeOf(Person) est une fonction. En fait, il s'agit de Function.prototype. Puisque Person a été créé en tant que fonction, il partage le même objet de fonction prototype que toutes les fonctions. C'est la même chose que Person.__proto__, mais cette propriété ne doit pas être utilisée. Quoi qu’il en soit, avec Object.getPrototypeOf(Person), vous montez effectivement l’échelle de ce que l’on appelle la chaîne du prototype.

La chaîne dans la direction ascendante ressemble à ceci:

PersonFunction.prototypeObject.prototype (point de fin)

Il est important de noter que cette chaîne de prototypes a peu à voir avec les objets que Person peut construire. Ces objets construits ont leur propre chaîne de prototype, et cette chaîne ne peut potentiellement avoir aucun ancêtre proche en commun avec celui mentionné ci-dessus.

Prenons par exemple cet objet:

var p = new Person();

p n'a pas de relation directe prototype-chaîne avec Personne. Leur relation est différente. L'objet p a sa propre chaîne de prototypes. En utilisant Object.getPrototypeOf, vous constaterez que la chaîne est la suivante:

pPerson.prototypeObject.prototype (point de fin)

Il n'y a pas d'objet de fonction dans cette chaîne (bien que cela puisse être).

Donc Person semble lié à deux types de chaînes, qui vivent leur propre vie. Pour "sauter" d'une chaîne à l'autre, vous utilisez:

  1. .prototype: saute de la chaîne du constructeur à la chaîne de l'objet créé. Cette propriété n'est donc définie que pour les objets fonction (car new ne peut être utilisé que sur des fonctions).

  2. .constructor: saute de la chaîne de l'objet créé à la chaîne du constructeur.

Voici une présentation visuelle des deux chaînes prototypes impliquées, représentées sous forme de colonnes:

 enter image description here

Pour résumer:

La propriété prototype ne donne aucune information sur la chaîne de prototypes sujet, mais sur les objets créés par le sujet. 

Il n’est pas surprenant que le nom de la propriété prototype puisse prêter à confusion. Il aurait peut-être été plus clair si cette propriété avait été nommée prototypeOfConstructedInstances ou quelque chose du même genre.

Vous pouvez basculer entre les deux chaînes de prototypes:

Person.prototype.constructor === Person

Cette symétrie peut être brisée en attribuant explicitement un objet différent à la propriété prototype (pour plus d'informations à ce sujet plus tard).

Créer une fonction, obtenir deux objets

Person.prototype est un objet créé en même temps que la fonction Person a été créée. Il a Person en tant que constructeur, même si ce constructeur n'a pas encore été exécuté. Donc, deux objets sont créés en même temps:

  1. La fonction Person elle-même
  2. L'objet qui servira de prototype lorsque la fonction sera appelée constructeur

Les deux sont des objets, mais ils ont des rôles différents: la fonction objet constructions, tandis que l'autre objet représente le prototype de tout objet que la fonction va construire. L'objet prototype deviendra le parent de l'objet construit dans sa chaîne de prototypes.

Puisqu'une fonction est aussi un objet, elle a également son propre parent dans sa propre chaîne de prototypes, mais rappelons que ces deux chaînes concernent des choses différentes. 

Voici quelques égalités qui pourraient aider à comprendre le problème - toutes ces informations sont imprimées true:

function Person() {};

// This is prototype chain info for the constructor (the function object):
console.log(Object.getPrototypeOf(Person) === Function.prototype);
// Step further up in the same hierarchy:
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
console.log(Person.__proto__ === Function.prototype);
// Here we swap lanes, and look at the constructor of the constructor
console.log(Person.constructor === Function);
console.log(Person instanceof Function);

// Person.prototype was created by Person (at the time of its creation)
// Here we swap lanes back and forth:
console.log(Person.prototype.constructor === Person);
// Although it is not an instance of it:
console.log(!(Person.prototype instanceof Person));
// Instances are objects created by the constructor:
var p = new Person();
// Similarly to what was shown for the constructor, here we have
// the same for the object created by the constructor:
console.log(Object.getPrototypeOf(p) === Person.prototype);
console.log(p.__proto__ === Person.prototype);
// Here we swap lanes, and look at the constructor
console.log(p.constructor === Person);
console.log(p instanceof Person);

Ajout de niveaux à la chaîne de prototypes

Bien qu'un objet prototype soit créé lorsque vous créez une fonction constructeur, vous pouvez ignorer cet objet et affecter un autre objet à utiliser comme prototype pour toute instance ultérieure créée par ce constructeur.

Par exemple:

function Thief() { }
var p = new Person();
Thief.prototype = p; // this determines the prototype for any new Thief objects:
var t = new Thief();

Maintenant, la chaîne de prototypes de t est un crans plus longue que celle de p:

tpPerson.prototypeObject.prototype (point de fin)

L'autre chaîne prototype n'est pas plus longue: Thief et Person sont des frères et soeurs partageant le même parent dans leur chaîne prototype:

Person}
Thief} → Function.prototypeObject.prototype (point de fin)

Le graphique présenté précédemment peut ensuite être étendu à ceci (le Thief.prototype original est omis):

 enter image description here

Les lignes bleues représentent les chaînes de prototypes, les autres lignes colorées représentent les autres relations:

  • entre un objet et son constructeur
  • entre un constructeur et l'objet prototype qui sera utilisé pour construire des objets
19
trincot

Le guide définitif du JavaScript orienté objet - explication vidéo très concise et claire de 30 minutes environ (le sujet relatif à l'héritage prototypal commence à partir de 5:45 , bien que je préfère écouter l'intégralité de la vidéo ). L’auteur de cette vidéo a également réalisé un site Web JavaScript avec les visualiseurs d’objets http://www.objectplayground.com/ .  enter image description here  enter image description here

16
Bad

J'ai trouvé utile d'expliquer la "chaîne du prototype" comme une convention récursive lorsque l'on fait référence à obj_n.prop_X:

si obj_n.prop_X n'existe pas, vérifiez obj_n+1.prop_Xobj_n+1 = obj_n.[[prototype]]

Si le prop_X est enfin trouvé dans le k-ème objet prototype, alors

obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X

Vous pouvez trouver un graphique de la relation des objets Javascript par leurs propriétés ici:

js objects graph

http://jsobjects.org

14
B M

Lorsqu'un constructeur crée un objet, cet objet fait référence implicitement à la propriété "prototype" du constructeur afin de résoudre les références de propriété. La propriété «prototype» du constructeur peut être référencée par l’expression de programme constructor.prototype et les propriétés ajoutées au prototype d’un objet sont partagées, par héritage, par tous les objets partageant le prototype.

13
Tom

Laissez-moi vous dire ma compréhension des prototypes. Je ne vais pas comparer l'héritage ici avec d'autres langues. J'aimerais que les gens arrêtent de comparer les langues et comprennent seulement la langue en tant que telle. Comprendre les prototypes et l'héritage des prototypes est si simple, je vais vous le montrer ci-dessous.

Le prototype est comme un modèle sur lequel vous créez un produit. Le point crucial à comprendre est que lorsque vous créez un objet en utilisant un autre objet comme prototype, le lien entre le prototype et le produit est permanent. Par exemple:

var model = {x:2};
var product = Object.create(model);
model.y = 5;
product.y
=>5

Chaque objet contient une propriété interne appelée [[prototype]], accessible par la fonction Object.getPrototypeOf(). Object.create(model) crée un nouvel objet et définit sa propriété [[prototype]] sur l'objet model . Par conséquent, lorsque vous exécuterez Object.getPrototypeOf(product), vous obtiendrez l'objet model .

Les propriétés du produit sont gérées de la manière suivante:

  • Lorsqu'une propriété est accédée pour lire sa valeur, elle est recherchée dans la chaîne d'étendue. La recherche de la variable commence à partir du product vers son prototype. Si une telle variable est trouvée dans la recherche, la recherche est arrêtée là et la valeur est renvoyée. Si une telle variable ne peut pas être trouvée dans la chaîne d'étendue, undefined est renvoyé.
  • Lorsqu'une propriété est écrite (modifiée), la propriété est toujours écrite sur l'objet product . Si le produit n'a pas déjà une telle propriété, il est implicitement créé et écrit.

Une telle liaison d'objets à l'aide de la propriété prototype est appelée héritage prototypal. Là, c'est si simple, d'accord?

10
Aravind

Considérons l'objet keyValueStore suivant:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
        this.get = function(key) { return this.data[key]; };
        this.set = function(key, value) { this.data[key] = value; };
        this.delete = function(key) { delete this.data[key]; };
        this.getLength = function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  { // Singleton public properties
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

Je peux créer une nouvelle instance de cet objet en procédant comme suit:

kvs = keyValueStore.create();

Chaque instance de cet objet aurait les propriétés publiques suivantes:

  • data
  • get 
  • set
  • delete
  • getLength

Supposons maintenant que nous créons 100 instances de cet objet keyValueStore. Même si get, set, delete, getLength fera exactement la même chose pour chacune de ces 100 instances, chaque instance possède sa propre copie de cette fonction.

Maintenant, imaginez si vous pouviez avoir une seule copie get, set, delete et getLength et que chaque instance fasse référence à cette même fonction. Cela serait meilleur pour la performance et nécessiterait moins de mémoire.

C'est là que les prototypes entrent en jeu. Un prototype est un "plan" de propriétés héritées mais non copiées par les instances. Cela signifie donc qu'il n'existe qu'une seule fois en mémoire pour toutes les instances d'un objet et qu'il est partagé par toutes ces instances.

Maintenant, considérons à nouveau l'objet keyValueStore. Je pourrais le réécrire comme ceci:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
    };

    kvs.prototype = {
        'get' : function(key) { return this.data[key]; },
        'set' : function(key, value) { this.data[key] = value; },
        'delete' : function(key) { delete this.data[key]; },
        'getLength' : function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  {
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

Ceci est EXACTEMENT identique à la version précédente de l’objet keyValueStore, à la différence près que toutes ses méthodes sont maintenant placées dans un prototype. Cela signifie que toutes les 100 instances partagent maintenant ces quatre méthodes au lieu que chacune d’elles ait sa propre copie.

9
John Slegers

Une autre tentative pour expliquer l'héritage basé sur un prototype JavaScript avec de meilleures images

 Simple objects inheritanse

8
rus1

Résumé:

  • Les fonctions sont des objets en javascript et peuvent donc avoir des propriétés
  • (Constructeur) fonctions toujours avoir une propriété prototype
  • Lorsqu'une fonction est utilisée en tant que constructeur avec le mot clé new, l'objet reçoit une propriété __proto__.
  • Cette propriété __proto__ fait référence à la propriété prototype de la fonction constructeur.

Exemple:

function Person (name) {
  this.name = name;
}

let me = new Person('willem');

console.log(Person.prototype) // Person has a prototype property

console.log(Person.prototype === me.__proto__) // the __proto__ property of the instance refers to prototype property of the function.

Pourquoi est-ce utile?

Lors de la recherche de propriétés sur les objets, Javascript a un mécanisme appelé 'héritage prototypal' , voici ce que fait fondamentalement:

  • First est coché si la propriété est située sur l'objet lui-même. Si c'est le cas, cette propriété est renvoyée.
  • Si la propriété n'est pas située sur l'objet lui-même, elle "montera le protocole". Il s’agit essentiellement de l’objet auquel fait référence la propriété proto . Là, il vérifie si la propriété est disponible sur l'objet référencé par proto
  • Si la propriété n'est pas située sur l'objet proto , elle remontera la chaîne proto jusqu'à l'objet Object.
  • S'il ne peut trouver la propriété nulle part sur l'objet et sa chaîne de prototypes, il retournera indéfini.

Par exemple:

function Person(name) {
  this.name = name;
}

let mySelf = new Person('Willem');

console.log(mySelf.__proto__ === Person.prototype);

console.log(mySelf.__proto__.__proto__ === Object.prototype);

4
Willem van der Veen

un autre schéma montrant les relations _PROTO_, prototype et constructeur:  enter image description here

3
IvanM

C'est juste que vous avez déjà un objet avec Object.new mais vous n'en avez toujours pas lorsque vous utilisez la syntaxe du constructeur.

2
shiva kumar

Le Prototype crée nouvel objet en clonant un objet existant objet . Ainsi, lorsque nous pensons au prototype, nous pouvons vraiment penser au clonage ou à la création à une copie de quelque chose au lieu de l'inventer.

2
Arif

Il est important de comprendre qu'il existe une distinction entre le prototype d'un objet (disponible via Object.getPrototypeOf(obj) ou via la propriété obsolète obj.__proto__) et la propriété prototype dans les fonctions du constructeur. Le premier est la propriété de chaque instance et le dernier est la propriété du constructeur. C'est-à-dire que Object.getPrototypeOf(new Foobar()) fait référence au même objet que Foobar.prototype.

Référence: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes

0
Baraa Al-Tabbaa