Какое значение имеет свойство конструктора Javascript? - PullRequest
117 голосов
/ 25 октября 2010

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

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`.

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

Bar.prototype.constructor = Bar;

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

Ответы [ 5 ]

98 голосов
/ 25 октября 2010

Первый шаг - понять, что такое 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. Я предполагаю, что идея состоит в том, чтобы сделать нечто похожее на языки, подобные C ++ / Java / C #, где поиск по типу (свойство constructor) всегда будет приводить к наиболее конкретному типу, а не к типу более общего объекта, который находится выше в прототип цепи.

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

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

70 голосов
/ 25 октября 2010

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

10 голосов
/ 27 марта 2012

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

  1. это одна из распространенных реализаций наследования:

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

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

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

2 голосов
/ 21 января 2016

Один из случаев, когда вы хотите, чтобы свойство 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.
0 голосов
/ 08 декабря 2015

Свойство конструктора указывает на конструктор, который использовался для создания экземпляра объекта.Если вы набрали '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'
...