Каково это значение свойства конструктора Javascript?

Пытаясь согнуть головой вокруг Javascript, возьмите OO... и, как и многие другие, запутались в свойстве constructor. В частности, значение свойства constructor, поскольку я не могу показать, что оно имеет какой-либо эффект. Например:.

function Foo(age) {
    this.age = age;
}

function Bar() {
    this.name = "baz"; 
}

Bar.prototype = new Foo(42); 
var b = new Bar;    

alert(b.constructor); // "Foo". That OK because we inherit `Foo` prototype.
alert(b.name);        // "baz". Shows that Bar() was called as constructor.
alert(b.age);         // "42", inherited from `Foo`.

В приведенном выше примере объект b, похоже, имеет правый конструктор, называемый (Bar) – и он наследует свойство age от Foo. Так почему многие люди предлагают это как необходимый шаг:

Bar.prototype.constructor = Bar;

Ясно, что при построении b был вызван конструктор Bar справа, так какое влияние имеет свойство этого прототипа? Мне любопытно узнать, какая практическая разница на самом деле делает свойство конструктора "правильным", поскольку я не вижу, чтобы это имело какое-либо влияние на конструкцию, которая фактически вызывается после создания объекта.

Ответ 1

Свойство constructor не имеет абсолютно никакого практического значения для чего-либо внутри. Это только любое использование, если ваш код явно использует его. Например, вы можете решить, что вам нужно, чтобы каждый из ваших объектов имел ссылку на фактическую конструкторскую функцию, которая его создала; если да, то вам необходимо установить constructor свойство явно при настройке наследования путем присвоения объекта конструктора функции prototype свойство, как в вашем примере.

Ответ 2

Шаг первый заключается в том, чтобы понять, что такое constructor и prototype. Это не сложно, но нужно отпустить "наследование" в классическом смысле.

Конструктор

Свойство constructor не вызывает никаких особых эффектов в вашей программе, за исключением того, что вы можете посмотреть на него, чтобы посмотреть, какая функция была использована в сочетании с оператором new для создания вашего объекта, Если вы набрали new Bar(), это будет Bar, и вы набрали new Foo, это будет Foo.

Прототип

Свойство prototype используется для поиска, если объект, о котором идет речь, не имеет запрашиваемого свойства. Если вы пишете x.attr, JavaScript попытается найти attr среди атрибутов x. Если он не сможет найти его, он будет выглядеть в x.__proto__. Если он не существует, он будет выглядеть в x.__proto__.__proto__ и так далее, пока будет определен __proto__.

Итак, что такое __proto__ и что он имеет отношение к prototype? Короче говоря, prototype предназначен для "типов", а __proto__ - для "экземпляров". (Я говорю это с кавычками, потому что нет никакой разницы между типами и экземплярами). Когда вы пишете x = new MyType(), что происходит (между прочим), это то, что x.__proto___ установлен на MyType.prototype.

Вопрос

Теперь, это должно быть все, что вам нужно, чтобы получить то, что ваш собственный пример означает, но попытаться ответить на ваш реальный вопрос; "зачем писать что-то вроде":

Bar.prototype.constructor = Bar;

Я лично его никогда не видел, и я нахожу его немного глупым, но в контексте, который вы ему дали, будет означать, что Bar.prototype -объект (созданный с помощью new Foo(42)) будет представлять, как создается Bar, а не Foo. Я полагаю, что идея состоит в том, чтобы сделать что-то похожее на языки С++/Java/С#, где тип-lookup (свойство constructor) всегда будет давать наиболее специфический тип, а не тип более общего объекта дальше в прототип цепи.

Мой совет: не думайте о "наследовании" в JavaScript. Понятия интерфейсов и миксинов имеют больше смысла. И не проверяйте объекты для их типов. Проверьте вместо этого требуемые свойства ( "если он ходит как утка и шарлатанцы, как утка, это утка" ).

Попытка заставить JavaScript превратиться в классическую модель наследования, когда все, что у него есть, является прототипом-механизмом, описанным выше, является причиной путаницы. Многие люди, которые предложили вручную установить constructor -property, вероятно, пытались сделать именно это. Абстракции прекрасны, но это ручное присваивание свойства constructor не очень идиоматическое использование JavaScript.

Ответ 3

один случай использования конструктора:

  • это одна из общих реализаций наследования:

    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;
        ...
    };
    
  • этот new f() не будет вызывать конструктор суперкласса, поэтому, когда вы создаете subClass, возможно, вам нужно сначала вызвать superClass, например:

    SubClass = function() {
        SubClass.superClass.constructor.call(this);
    };
    

поэтому свойство конструктора имеет смысл здесь.

Ответ 4

Один из вариантов использования, когда вы хотите, чтобы свойство prototype.constructor выдержало переопределение свойств prototype, - это когда вы определяете метод в prototype, который создает новые экземпляры того же типа, что и данный экземпляр. Пример:

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.

Ответ 5

Свойство конструктора указывает на конструктор, который использовался для создания экземпляра объекта. Если вы набрали "new Bar()", это будет "Bar", и вы набрали "new Foo()", это будет "Foo".

Но если вы установите прототип без установки конструктора, вы получите что-то вроде этого:

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'

Чтобы установить конструктор на самом деле в конструктор, который использовался для создания объекта, нам также нужно установить конструктор и установить прототип следующим образом:

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'