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?
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.
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.
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);
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.
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:
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.
-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
.
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.
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:
var newObject = {};
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"
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.
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.
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, ...)
obj.__proto__
obj.__proto__.__proto__
, etc.__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-leLe 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
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.
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:
__proto__
.prototype
.new
sont liés à la propriété prototype
de leur constructeur.prototype
sera inutilisée.new
.Javascript n'a pas d'héritage au sens habituel, mais il a la chaîne de prototypes.
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.
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.
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:
Person
→ Function.prototype
→ Object.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:
p
→ Person.prototype
→ Object.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:
.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).
.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:
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).
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:
Person
elle-mêmeLes 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);
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:
t
→ p
→ Person.prototype
→ Object.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.prototype
→ Object.prototype
(point de fin)
Le graphique présenté précédemment peut ensuite être étendu à ceci (le Thief.prototype
original est omis):
Les lignes bleues représentent les chaînes de prototypes, les autres lignes colorées représentent les autres relations:
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/ .
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_X
où obj_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:
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.
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:
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?
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.
Une autre tentative pour expliquer l'héritage basé sur un prototype JavaScript avec de meilleures images
new
, l'objet reçoit une propriété __proto__
.__proto__
fait référence à la propriété prototype
de la fonction constructeur.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.
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:
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);
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.
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.
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