web-dev-qa-db-fra.com

Quelle est la signification de la propriété constructeur Javascript?

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.

117
aaa90210

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.

70
Tim Down

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é constructorne 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.

98
Jakob

un cas pour utiliser le constructeur:

  1. 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;
        ...
    };
    
  2. 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.

10
Jack Hu

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.
2
golem

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'
0
jsbisht