Essayer de contourner la position de Javascript sur OO ... et, comme beaucoup d'autres, confondre la propriété constructor
. En particulier, la signification de la propriété constructor
, car je n'arrive pas à lui donner d'effet. Par exemple.:
function Foo(age) {
this.age = age;
}
function Bar() {
Foo.call(this, 42);
this.name = "baz";
}
Bar.prototype = Object.create(Foo.prototype);
var b = new Bar;
alert(b.constructor); // "Foo". That's OK because we inherit `Foo`'s prototype.
alert(b.name); // "baz". Shows that Bar() was called as constructor.
alert(b.age); // "42", inherited from `Foo`.
Dans l'exemple ci-dessus, l'objet b
semble avoir eu le bon constructeur appelé (Bar
) - et il hérite de la propriété age de Foo
. Alors pourquoi beaucoup de gens suggèrent-ils cela comme une étape nécessaire:
Bar.prototype.constructor = Bar;
De toute évidence, le bon constructeur Bar
était appelé lors de la construction de b
, alors quel impact cette propriété prototype at-elle? Je suis curieux de savoir quelle différence pratique cela fait réellement d'avoir la propriété constructeur définie "correctement" - car je ne vois pas que cela affecte le constructeur qui est réellement appelé après la création d'un objet.
La propriété constructor
ne fait aucune différence pratique avec quoi que ce soit en interne. Il n'est utile que si votre code l'utilise explicitement. Par exemple, vous pouvez décider que vous avez besoin que chacun de vos objets ait une référence à la fonction constructeur réelle qui l'a créé; si c'est le cas, vous devrez définir la propriété constructor
explicitement lorsque vous configurez l'héritage en affectant un objet à la propriété prototype
d'une fonction constructeur, comme dans votre exemple.
La première étape consiste à comprendre en quoi consistent constructor
et prototype
. Ce n'est pas difficile, mais il faut abandonner l '"héritage" au sens classique.
Le constructeur
La propriété constructor
ne fait pas provoque des effets particuliers dans votre programme, sauf que vous pouvez l'examiner pour voir quelle fonction a été utilisée en conjonction avec l'opérateur new
pour créez votre objet. Si vous avez tapé new Bar()
ce sera Bar
et vous aurez tapé new Foo
Ce sera Foo
.
Le prototype
La propriété prototype
est utilisée pour la recherche dans le cas où l'objet en question n'a pas la propriété demandée. Si vous écrivez x.attr
, JavaScript essaiera de trouver attr
parmi les attributs de x
. S'il ne le trouve pas, il cherchera dans x.__proto__
. S'il n'est pas là non plus, il apparaîtra dans x.__proto__.__proto__
Et ainsi de suite tant que __proto__
Est défini.
Alors, qu'est-ce que __proto__
Et qu'est-ce que cela a à voir avec prototype
? En bref, prototype
est pour les "types" tandis que __proto__
Est pour les "instances". (Je dis cela avec des guillemets car il n'y a pas vraiment de différence entre les types et les instances). Lorsque vous écrivez x = new MyType()
, ce qui se produit (entre autres), c'est que x.__proto___
Est défini sur MyType.prototype
.
La question
Maintenant, ce qui précède devrait être tout ce dont vous avez besoin pour tirer ce que signifie votre propre exemple, mais pour essayer de répondre à votre question réelle; "pourquoi écrire quelque chose comme":
Bar.prototype.constructor = Bar;
Personnellement, je ne l'ai jamais vu et je le trouve un peu idiot, mais dans le contexte que vous avez donné, cela signifiera que l'objet Bar.prototype
- (créé en utilisant new Foo(42)
) se posera comme ont été créés par Bar
plutôt que Foo
. Je suppose que l'idée est que certains créent quelque chose de similaire aux langages de type C++/Java/C # où une recherche de type (la propriété constructor
) donnera toujours le type le plus spécifique plutôt que le type de l'objet plus générique. dans la chaîne de prototypes.
Mon conseil: ne pensez pas beaucoup à "l'héritage" en JavaScript. Les concepts d'interfaces et de mixins ont plus de sens. Et ne vérifiez pas les objets pour leurs types. Vérifiez plutôt les propriétés requises ("s'il marche comme un canard et quacks comme un canard, c'est un canard").
Essayer de forcer JavaScript dans un modèle d'héritage classique, alors qu'il ne possède que le prototype-mécanisme décrit ci-dessus, est à l'origine de la confusion. Les nombreuses personnes qui ont suggéré de définir manuellement la propriété constructor
- ont probablement essayé de le faire. Les abstractions sont bien, mais cette affectation manuelle de la propriété constructeur n'est pas une utilisation très idiomatique de JavaScript.
un cas pour utiliser le constructeur:
c'est l'une des réalisations communes de l'héritage:
Function.prototype.extend = function(superClass,override) {
var f = new Function();
f.prototype = superClass.prototype;
var p = this.prototype = new f();
p.constructor = this;
this.superclass = superClass.prototype;
...
};
cette new f()
n'appellerait pas le constructeur de superClass, donc quand vous créez une sous-classe, vous devrez peut-être appeler la superClasse au début, comme ceci:
SubClass = function() {
SubClass.superClass.constructor.call(this);
};
donc la propriété constructeur a un sens ici.
L'un des cas d'utilisation où vous souhaitez que le prototype.constructor
la propriété pour survivre prototype
la réaffectation de propriété consiste à définir une méthode sur le prototype
qui produit de nouvelles instances du même type que l'instance donnée. Exemple:
function Car() { }
Car.prototype.orderOneLikeThis = function() { // Clone producing function
return new this.constructor();
}
Car.prototype.advertise = function () {
console.log("I am a generic car.");
}
function BMW() { }
BMW.prototype = Object.create(Car.prototype);
BMW.prototype.constructor = BMW; // Resetting the constructor property
BMW.prototype.advertise = function () {
console.log("I am BMW with lots of uber features.");
}
var x5 = new BMW();
var myNewToy = x5.orderOneLikeThis();
myNewToy.advertise(); // => "I am BMW ..." if `BMW.prototype.constructor = BMW;` is not
// commented; "I am a generic car." otherwise.
La propriété constructeur pointe vers le constructeur qui a été utilisé pour créer l'instance d'objet. Si vous avez tapé 'new Bar ()' ce sera 'Bar' et vous avez tapé 'new Foo ()' ce sera 'Foo'.
Mais si vous définissez le prototype sans définir le constructeur, vous obtiendrez quelque chose comme ceci:
function Foo(age) {
this.age = age;
}
function Bar() {
this.name = "baz";
}
Bar.prototype = new Foo(42);
var one = new Bar();
console.log(one.constructor); // 'Foo'
var two = new Foo();
console.log(two.constructor); // 'Foo'
Pour définir réellement le constructeur sur le constructeur qui a été utilisé pour créer l'objet, nous devons également définir le constructeur tout en définissant le prototype comme suit:
function Foo(age) {
this.age = age;
}
function Bar() {
this.name = "baz";
}
Bar.prototype = new Foo(42);
Bar.prototype.constructor = Bar;
var one = new Bar();
console.log(one.constructor); // 'Bar'
var two = new Foo();
console.log(two.constructor); // 'Foo'