Pourquoi déclarer des propriétés sur le prototype pour les variables d'instance en JavaScript
J'essaie de comprendre cet art noir appelé JavaScript - et, je dois l'avouer, plutôt enthousiasmé. Je regarde des exemples de code, principalement de "easeljs", car c’est ce que je vais principalement utiliser. Et je suis un peu confus ..
Je pense (je crois comprendre) la différence entre l’utilisation de prototype
pour les fonctions ou propriétés qui sont des variables class
et l’utilisation de this.someProp
pour les variables 'instance' (Oui, je comprends qu’il n’existe pas de classes en JavaScript.)
Le code que j'ai examiné et que j'utilise comme modèles pour mon propre code, des variables declare
prototype
, auxquelles il se réfère ensuite, c'est-à-dire i.e.
Dans le constructeur:
this.name = name;
Puis une déclaration:
Object.prototype.name;
Et ensuite,
this.name = "Freddy";
Ceci est dans les fonctions appelées avec 'new', alors dans ce cas, si je comprends bien, this
fait référence à l'objet actuel. Ce qui me laisse perplexe, c’est ce que fait la déclaration de prototype et pourquoi l’utilisons-nous pour des variables d’exemple?
Clarification : Dans le code suivant, je ne vois pas ce que le prototype déclaration de radius permet d'obtenir:
(function(){
// constructor
function MyCircle(radius){
this.radius = radius;
}
MyCircle.prototype.radius;
this.area = function(){
return 3.14*this.radius*this.radius;
};
window.MyCircle = MyCircle;
}());
La valeur sur un prototype a un comportement de clé différent de celui défini sur une propriété directement sur l'instance. Essaye ça:
// Create a constructor
function A() {}
// Add a prototype property
A.prototype.name = "Freddy";
// Create two object instances from
// the constructor
var a = new A();
var b = new A();
// Both instances have the property
// that we created on the prototype
console.log(a.name); // Freddy
console.log(b.name); // Freddy
// Now change the property on the
// prototype
A.prototype.name = "George";
// Both instances inherit the change.
// Really they are just reading the
// same property from the prototype
// rather than their own property
console.log(a.name); // George
console.log(b.name); // George
Cela ne serait pas possible sans héritage prototypique.
Vous pouvez tester si la propriété est la propriété instances ou la propriété prototype à l'aide de la méthode hasOwnProperty
.
console.log(a.hasOwnProperty("name")); // false
Une instance peut remplacer la valeur prototype
.
b.name = "Chris";
console.log(b.hasOwnProperty("name")); // true
console.log(a.name); // George
console.log(b.name); // Chris
Et revenez à la valeur prototype
.
delete b.name;
console.log(b.hasOwnProperty("name")); // false
console.log(b.name); // George
Ceci est une partie puissante de l'héritage prototype.
Dans l'autre motif:
function A() {
this.name = "George";
}
La variable this.name
est à nouveau déclarée à chaque nouvelle instance.
Il est logique que les méthodes soient déclarées comme fonctions sur le prototype. Plutôt que de redéclarer la définition de la fonction à chaque instance, toutes les instances peuvent partager une seule fonction.
En termes de variables, plutôt que de fonctions, le prototype peut éventuellement être utilisé pour les valeurs par défaut dans le cas où une instance ne définit pas sa propre valeur.
Une valeur stockée sur le prototype fournit une valeur default pour cette propriété.
Si vous écrivez ensuite une valeur dans cette propriété, l'instance instance acquerra cette nouvelle valeur en masquant la valeur du prototype, qui restera intacte.
Dans le contexte du code que vous avez maintenant ajouté à la question:
MyCircle.prototype.radius;
ne fait absolument rien. C'est un non-op - il tente de lire cette propriété, puis rejette le résultat.
D'autres réponses ont déjà expliqué la différence entre les propriétés prototype et d'instance.
Mais juste pour ajouter à la réponse, décomposons votre extrait de code:
(function(){ // <------- 1
// constructor
function MyCircle(radius){ // <------- 2
this.radius = radius; // <------- 2.1
}
MyCircle.prototype.radius; // <------- 3
this.area = function(){ // <------- 4
return 3.14*this.radius*this.radius;
};
window.MyCircle = MyCircle; // <------- 5
}());
- Création d'une
IIFE
qui agit comme un conteneur de portée pour le code interne - Déclarer une fonction appelée
MyCircle
à l'aide d'un modèle constructeur (mais notez qu'elle n'est jamais "construite" et devrait donc probablement supprimer la lettre majuscule car elle induit en erreur)- lorsque invoqué crée une propriété d'instance
radius
sur l'objet appelé
- lorsque invoqué crée une propriété d'instance
- Tenter d'accéder à une propriété
radius
sur la variableMyCircle
de la fonctionprototype
qui n'existe pas, a pour résultatundefined
- Créer une propriété d'instance
area
sur l'objet de fenêtre global et lui attribuer une expression de fonction - Création d'une propriété d'instance
MyCircle
sur un objetwindow
et affectation de la fonctionMyCircle
Résumé: On dirait qu'il crée une propriété area
et MyCircle
sur l'objet global window
et que, lorsque MyCircle
est appelé, il crée une propriété supplémentaire radius
.
Utilisation: MyCircle doit être invoqué avant area puisque celui-ci repose sur l'initialisation du rayon par MyCircle:
window.MyCircle(10);
window.area(); // evaluates to 314
Oui, je conviens que le prototype peut être utilisé pour les valeurs par défaut des propriétés (variables). La fonction constructeur n'a pas besoin de déclarer une propriété; cela peut être fait conditionnellement.
function Person( name, age ) {
this.name = name;
if ( age ) {
this.age = age;
}
}
Person.prototype.sayHello = function() {
console.log( 'My name is ' + this.name + '.' );
};
Person.prototype.sayAge = function() {
if ( this.age ) {
console.log( 'I am ' + this.age + ' yrs old!' );
} else {
console.log( 'I do not know my age!' );
}
};
Person.prototype.age = 0.7;
//-----------
var person = new Person( 'Lucy' );
console.log( 'person.name', person.name ); // Lucy
console.log( 'person.age', person.age ); // 0.7
person.sayAge(); // I am 0.7 yrs old!
Voyez comment la variable age
de Lucy est déclarée et initialisée sous condition.