Почему нам нужно установить DerivedObject.prototype.constructor в DerivedObject? [Дубликат]

Когда вы объявляете ссылочную переменную (т. е. объект), вы действительно создаете указатель на объект. Рассмотрим следующий код, в котором вы объявляете переменную примитивного типа int:

int x;
x = 10;

В этом примере переменная x является int, и Java инициализирует ее для 0. Когда вы назначаете его 10 во второй строке, ваше значение 10 записывается в ячейку памяти, на которую указывает x.

Но когда вы пытаетесь объявить ссылочный тип, произойдет что-то другое. Возьмите следующий код:

Integer num;
num = new Integer(10);

Первая строка объявляет переменную с именем num, но она не содержит примитивного значения. Вместо этого он содержит указатель (потому что тип Integer является ссылочным типом). Поскольку вы еще не указали, что указать на Java, он устанавливает значение null, что означает «Я ничего не указываю».

Во второй строке ключевое слово new используется для создания экземпляра (или создания ) объекту типа Integer и переменной указателя num присваивается этот объект. Теперь вы можете ссылаться на объект, используя оператор разыменования . (точка).

Exception, о котором вы просили, возникает, когда вы объявляете переменную, но не создавали объект. Если вы попытаетесь разыменовать num. Перед созданием объекта вы получите NullPointerException. В самых тривиальных случаях компилятор поймает проблему и сообщит вам, что «num не может быть инициализирован», но иногда вы пишете код, который непосредственно не создает объект.

Например, вы можете имеют следующий метод:

public void doSomething(SomeObject obj) {
   //do something to obj
}

В этом случае вы не создаете объект obj, скорее предполагая, что он был создан до вызова метода doSomething. К сожалению, этот метод можно вызвать следующим образом:

doSomething(null);

В этом случае obj имеет значение null. Если метод предназначен для того, чтобы что-то сделать для переданного объекта, целесообразно бросить NullPointerException, потому что это ошибка программиста, и программисту понадобится эта информация для целей отладки.

Альтернативно, там могут быть случаи, когда цель метода заключается не только в том, чтобы работать с переданным в объекте, и поэтому нулевой параметр может быть приемлемым. В этом случае вам нужно будет проверить нулевой параметр и вести себя по-другому. Вы также должны объяснить это в документации. Например, doSomething может быть записано как:

/**
  * @param obj An optional foo for ____. May be null, in which case 
  *  the result will be ____.
  */
public void doSomething(SomeObject obj) {
    if(obj != null) {
       //do something
    } else {
       //do something else
    }
}

Наконец, Как определить исключение & amp; причина использования Трассировки стека

263
задан Joshua Taylor 29 April 2015 в 13:34
поделиться

12 ответов

Это не всегда необходимо, но у него есть свои возможности. Предположим, мы хотели создать метод копирования на базовом классе Person. Например:

// define the Person Class  
function Person(name) {
    this.name = name;
}  

Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new this.constructor(this.name);
};  

// define the Student class  
function Student(name) {  
    Person.call(this, name);
}  

// inherit Person  
Student.prototype = Object.create(Person.prototype);

Теперь, что происходит, когда мы создаем новый Student и копируем его?

var student1 = new Student("trinth");  
console.log(student1.copy() instanceof Student); // => false

Копия не является экземпляром Student. Это связано с тем, что (без явных проверок) мы не смогли бы вернуть копию Student из «базового» класса. Мы можем вернуть только Person. Однако, если бы мы сбросили конструктор:

// correct the constructor pointer because it points to Person  
Student.prototype.constructor = Student;

... тогда все работает так, как ожидалось:

var student1 = new Student("trinth");  
console.log(student1.copy() instanceof Student); // => true
232
ответ дан Jez 19 August 2018 в 17:02
поделиться
  • 1
    Примечание. Атрибут constructor не имеет особого значения в JS, поэтому вы можете также назвать его bananashake. Единственное различие заключается в том, что двигатель автоматически инициализирует constructor на f.prototype всякий раз, когда вы объявляете функцию f. Однако он может быть перезаписан в любое время. – user123444555621 12 December 2011 в 23:11
  • 2
    @ Pumbaa80 - я получаю вашу точку зрения, но тот факт, что движок автоматически инициализируется constructor, означает, что имеет особый смысл в JS, в значительной степени по определению. – Wayne Burkett 19 January 2012 в 17:29
  • 3
    Я просто хочу пояснить, почему причина, по которой вы говорили, работает, потому что вы используете return new this.constructor(this.name); вместо return new Person(this.name);. Поскольку функция this.constructor является функцией Student (поскольку вы устанавливаете ее с помощью Student.prototype.constructor = Student;), функция copy завершает вызов функции Student. Я не уверен, что вы сделали с комментарием //just as bad. – CEGRD 8 December 2012 в 01:16
  • 4
    @lwburk, что вы подразумеваете под символом «// так же плохо»? – CEGRD 13 December 2012 в 23:29
  • 5
    Кажется, я понял. Но что, если конструктор Student добавил дополнительный аргумент, например: Student(name, id)? Нужно ли нам переопределить функцию copy, вызвав из нее версию Person, а затем также скопировать дополнительное свойство id? – snapfractalpop 7 March 2014 в 04:50

До сих пор путаница все еще существует.

Следуя исходному примеру, поскольку у вас есть существующий объект student1 как:

var student1 = new Student("Janet", "Applied Physics");

Предположим, вы не хотите знаете, как student1 создан, вам просто нужен другой объект, как он, вы можете использовать свойство конструктора для student1, например:

var student2 = new student1.constructor("Mark", "Object-Oriented JavaScript");

Здесь он не сможет получить свойства из Student если свойство конструктора не задано. Скорее это создаст объект Person.

5
ответ дан André Chalella 19 August 2018 в 17:02
поделиться

EDIT, я был на самом деле неправ. Комментирование строки не изменяет ее поведение вообще. (Я тестировал его)


Да, это необходимо. Когда вы выполняете

,
Student.prototype = new Person();  

Student.prototype.constructor становится Person. Поэтому вызов Student() возвращает объект, созданный Person. Если вы затем выполняете

Student.prototype.constructor = Student; 

Student.prototype.constructor, сбрасывается на Student. Теперь, когда вы вызываете Student(), он выполняет Student, который вызывает родительский конструктор Parent(), он возвращает правильно наследуемый объект. Если вы не сбросили Student.prototype.constructor перед его вызовом, вы получили бы объект, у которого не было бы никаких свойств, установленных в Student().

0
ответ дан Bojangles 19 August 2018 в 17:02
поделиться
  • 1
    Конструкция прототипа может стать человеком, но это уместно, поскольку оно наследует все свойства и методы от Лица. Создание нового Student () без установки prototype.constructor соответствующим образом вызывает его собственный конструктор. – Stephen 10 December 2011 в 04:29

TLDR; Не очень необходимо, но, вероятно, поможет в долгосрочной перспективе, и это более точно.

ПРИМЕЧАНИЕ. Многое редактировалось, поскольку мой предыдущий ответ был смущенно написан и имел некоторые ошибки, которые я пропустил в моей спешке, чтобы ответить. Спасибо тем, кто указал на некоторые вопиющие ошибки.

В принципе, для правильного подключения к подклассу в Javascript. Когда мы подклассом, мы должны сделать некоторые напуганные вещи, чтобы убедиться, что прототипная делеция работает правильно, включая перезапись объекта prototype. Перезапись объекта prototype включает в себя constructor, поэтому нам нужно исправить ссылку.

Давайте быстро рассмотрим, как работают «классы» в ES5.

Допустим, вы имеют конструкторскую функцию и ее прототип:

//Constructor Function
var Person = function(name, age) {
  this.name = name;
  this.age = age;
}

//Prototype Object - shared between all instances of Person
Person.prototype = {
  species: 'human',
}

Когда вы вызываете конструктор для создания экземпляра, скажем Adam:

// instantiate using the 'new' keyword
var adam = new Person('Adam', 19);

Ключевое слово new вызывается с помощью «Человек» 'в основном будет запускать конструктор Person с несколькими дополнительными строками кода:

function Person (name, age) {
  // This additional line is automatically added by the keyword 'new'
  // it sets up the relationship between the instance and the prototype object
  // So that the instance will delegate to the Prototype object
  this = Object.create(Person.prototype);

  this.name = name;
  this.age = age;

  return this;
}

/* So 'adam' will be an object that looks like this:
 * {
 *   name: 'Adam',
 *   age: 19
 * }
 */

Если мы console.log(adam.species), поиск не будет выполняться на экземпляре adam и будет искать прототипную цепочку для его .prototype, который есть Person.prototype - и Person.prototype , имеет свойство a .species, поэтому поиск будет успешным на Person.prototype. Затем он будет записывать 'human'.

Здесь Person.prototype.constructor будет правильно указывать на Person.

Итак, теперь интересная часть - так называемое «подклассификация». Если мы хотим создать класс Student, являющийся подклассом класса Person с некоторыми дополнительными изменениями, нам нужно убедиться, что Student.prototype.constructor указывает на точность для ученика.

Это не делается само по себе. При подклассе код выглядит следующим образом:

var Student = function(name, age, school) {
 // Calls the 'super' class, as every student is an instance of a Person
 Person.call(this, name, age);
 // This is what makes the Student instances different
 this.school = school
}

var eve = new Student('Eve', 20, 'UCSF');

console.log(Student.prototype); // this will be an empty object: {}

Вызов new Student() здесь возвращает объект со всеми требуемыми свойствами. Здесь, если мы проверим eve instanceof Person, он вернется false. Если мы попытаемся получить доступ к eve.species, он вернет undefined.

Другими словами, нам нужно связать делегирование, чтобы eve instanceof Person возвращало true и чтобы экземпляры делегата Student правильно Student.prototype, а затем Person.prototype.

НО, так как мы вызываем его с ключевым словом new, помните, что добавляет этот призыв? Он назовет Object.create(Student.prototype), как мы установили это делегиальное отношение между Student и Student.prototype. Обратите внимание, что прямо сейчас Student.prototype пуст. Таким образом, при поиске .species экземпляр Student потерпит неудачу, поскольку он делегирует только Student.prototype, а свойство .species не существует на Student.prototype.

Когда мы присваиваем Student.prototype Object.create(Person.prototype), сам Student.prototype делегирует Person.prototype, и поиск eve.species вернет human, как мы ожидаем. Предположительно, мы хотели бы, чтобы он наследовал от Student.prototype AND Person.prototype. Поэтому нам нужно все это исправить.

/* This sets up the prototypal delegation correctly 
 *so that if a lookup fails on Student.prototype, it would delegate to Person's .prototype
 *This also allows us to add more things to Student.prototype 
 *that Person.prototype may not have
 *So now a failed lookup on an instance of Student 
 *will first look at Student.prototype, 
 *and failing that, go to Person.prototype (and failing /that/, where do we think it'll go?)
*/
Student.prototype = Object.create(Person.prototype);

Теперь делегация работает, но мы переписываем Student.prototype с помощью функции Person.prototype. Поэтому, если мы назовем Student.prototype.constructor, он будет указывать на Person вместо Student. Вот почему мы должны это исправить.

// Now we fix what the .constructor property is pointing to    
Student.prototype.constructor = Student

// If we check instanceof here
console.log(eve instanceof Person) // true

В ES5 наше свойство constructor является ссылкой, которая ссылается на функцию, которую мы написали с намерением стать «конструктором». Помимо того, что дает нам ключевое слово new, конструктор в противном случае является «простой» функцией.

В ES6 constructor теперь встроен в способ, которым мы пишем классы - как в, он предоставляется как метод, когда мы объявляем класс. Это просто синтаксический сахар, но он предоставляет нам некоторые удобства, такие как доступ к super, когда мы расширяем существующий класс. Поэтому мы должны написать вышеприведенный код следующим образом:

class Person {
  // constructor function here
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  // static getter instead of a static property
  static get species() {
    return 'human';
  }
}

class Student extends Person {
   constructor(name, age, school) {
      // calling the superclass constructor
      super(name, age);
      this.school = school;
   }
}
9
ответ дан bthehuman 19 August 2018 в 17:02
поделиться
  • 1
    eve instanceof Student возвращен true. См. stackoverflow.com/questions/35537995/… для объяснения. Также, когда вы говорите which is, at the moment, nothing, о чем вы говорите? Каждая функция имеет прототип, поэтому, если я проверю Student.prototype, это что-то. – Aseem Bansal 27 February 2016 в 02:28
  • 2
    Виноват. Он должен был прочитать «eve instanceof Person», который вернет false. Я исправлю эту часть. Вы правы, что каждая функция имеет свойство прототипа. Однако , не присваивая прототипу Object.create(Person.prototype), Student.prototype пуст. Поэтому, если мы запишем eve.species, он не будет делегировать должным образом своему суперклассу Person, и он не будет записывать 'human'. Предположительно, мы хотим, чтобы каждый подкласс наследовал от своего прототипа, а также прототипа супер. – bthehuman 27 February 2016 в 18:49
  • 3
    Чтобы уточнить, which is, at the moment, nothing я имел в виду, что объект Student.prototype пуст. – bthehuman 27 February 2016 в 18:55
  • 4
    Подробнее о прототипе: без присвоения Student.prototype - Object.create(Person.prototype) - который, если вы помните, так же, как все экземпляры Person настроены на делегирование Person.prototype - поиск свойства на экземпляре Student делегирует только только Student.prototype. Таким образом, eve.species не сможет выполнить поиск. Если мы его назначим, Student.prototype сам делегирует Person.prototype, и поиск eve.species вернет human. – bthehuman 27 February 2016 в 19:02
  • 5
    Кажется, здесь есть несколько ошибок: & quot; Это необходимо, когда вы пытаетесь подражать подклассу [...], так что, когда вы проверяете, является ли экземпляр instance, «подкласс» Constructor , он будет точным. & quot; Нет, instanceof не использует constructor. "Однако, если мы посмотрим на .prototype.constructor, он все равно укажет на Person & quot; Nope, это будет Student. Я не понимаю смысла этого примера. Вызов функции в конструкторе не является наследованием. "В ES6 конструктор теперь является фактической функцией вместо ссылки на функцию" Что это? – Felix Kling 27 February 2016 в 19:03

Это огромная ошибка, если вы написали

Student.prototype.constructor = Student;

, но тогда, если бы существовал Учитель, чей прототип был также Person, и вы написали

Teacher.prototype.constructor = Teacher;

, тогда Студент конструктор теперь учитель!

Редактировать: вы можете избежать этого, убедившись, что вы установили прототипы ученика и учителя, используя новые экземпляры класса Person, созданные с использованием Object.create, как в примере Mozilla.

Student.prototype = Object.create(Person.prototype);
Teacher.prototype = Object.create(Person.prototype);
8
ответ дан James D 19 August 2018 в 17:02
поделиться
  • 1
    Student.prototype = Object.create(...) предполагается в этом вопросе. Этот ответ не добавляет ничего, кроме возможной путаницы. – André Chalella 6 November 2015 в 22:14
  • 2
    @ AndréNeves Я нашел этот ответ полезным. Object.create(...) используется в статье MDN, которая породила вопрос, но не в самом вопросе. Я уверен, что многие люди не щелкают. – Alex Ross 9 January 2016 в 03:10
  • 3
    этот ответ, вероятно, заслуживает большего внимания :) – Alexander Mills 3 November 2016 в 07:11
  • 4
    Связанная статья, на которую ссылается в вопросе alreay, использует Object.create (). Этот ответ и «Редактировать ответ» на самом деле не актуальны, и это смущает сказать наименьшее :-) – Drenai 29 December 2016 в 13:15
  • 5
    Более широкая точка заключается в том, что есть gotchas, которые уловят людей, новых для прототипов Javascript. Если мы обсудим в 2016 году, вы должны действительно использовать классы ES6, Babel и / или TypScript. Но если вы действительно хотите вручную создать классы таким образом, это поможет понять, как цепочки прототипов действительно работают, чтобы использовать свою силу. Вы можете использовать любой объект в качестве прототипа, и, возможно, вам не нужен новый отдельный объект. Кроме того, еще до того, как HTML 5 был полностью распространен, Object.create не всегда был доступен, поэтому было проще настроить класс неправильно. – James D 2 January 2017 в 23:40

Для простой функции конструктора:

function Person(){
    this.name = 'test';
}


console.log(Person.prototype.constructor) // function Person(){...}

Person.prototype = { //constructor in this case is Object
    sayName: function(){
        return this.name;
    }
}

var person = new Person();
console.log(person instanceof Person); //true
console.log(person.sayName()); //test
console.log(Person.prototype.constructor) // function Object(){...}

По умолчанию (из спецификации https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects / Object / constructor ), все прототипы автоматически получают свойство, называемое конструктором, которое указывает на функцию, на которой это свойство. В зависимости от конструктора к прототипу могут быть добавлены другие свойства и методы, которые не являются очень распространенной практикой, но все же разрешены для расширений.

Итак, просто отвечая: нам нужно убедиться, что значение в prototype.constructor правильно установлено, поскольку предполагается, что спецификация будет.

Должны ли мы всегда правильно устанавливать это стоимость? Это помогает при отладке и делает внутреннюю структуру совместимой со спецификацией. Мы должны определенно, когда наш API будет использоваться третьими сторонами, но не совсем, когда код будет окончательно выполнен во время выполнения.

0
ответ дан kospiotr 19 August 2018 в 17:02
поделиться

Это необходимо, если вам нужна альтернатива toString без monkeypatching:

//Local
foo = [];
foo.toUpperCase = String(foo).toUpperCase;
foo.push("a");
foo.toUpperCase();

//Global
foo = [];
window.toUpperCase = function (obj) {return String(obj).toUpperCase();}
foo.push("a");
toUpperCase(foo);

//Prototype
foo = [];
Array.prototype.toUpperCase = String.prototype.toUpperCase;
foo.push("a");
foo.toUpperCase();

//toString alternative via Prototype constructor
foo = [];
Array.prototype.constructor = String.prototype.toUpperCase;
foo.push("a,b");
foo.constructor();

//toString override
var foo = [];
foo.push("a");
var bar = String(foo);
foo.toString = function() { return bar.toUpperCase(); }
foo.toString();

//Object prototype as a function
Math.prototype = function(char){return Math.prototype[char]};
Math.prototype.constructor = function() 
  {
  var i = 0, unicode = {}, zero_padding = "0000", max = 9999;
  
  while (i < max) 
    {
    Math.prototype[String.fromCharCode(parseInt(i, 16))] = ("u" + zero_padding + i).substr(-4);

    i = i + 1;
    }    
  }

Math.prototype.constructor();
console.log(Math.prototype("a") );
console.log(Math.prototype["a"] );
console.log(Math.prototype("a") === Math.prototype["a"]);

1
ответ дан Paul Sweatte 19 August 2018 в 17:02
поделиться
  • 1
    Что из этого должно делать? foo.constructor() ?? – Ry-♦ 13 May 2018 в 23:24

Это необязательно. Это всего лишь одна из многих традиций, чемпионы ООП, чтобы попытаться превратить прототипное наследование JavaScript в классическое наследование. Единственное, что делает следующий

Student.prototype.constructor = Student; 

, это то, что теперь у вас есть ссылка на текущий «конструктор».

В ответе Уэйн, который был отмечен как правильный, вы можете сделать то же самое, что следующий код делает

Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new this.constructor(this.name);
};  

с кодом ниже (просто замените this.constructor на Person)

Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new Person(this.name);
}; 

Слава Богу, что с ES6 classic Натураторы-пуристы могут использовать собственные операторы языка, такие как класс, extends и super, и нам не нужно видеть, как исправления prototype.constructor и родительские рефери.

-1
ответ дан Roumelis George 19 August 2018 в 17:02
поделиться

Я бы не согласился. Нет необходимости устанавливать прототип. Возьмите тот же самый код, но удалите строку prototype.constructor. Что-нибудь изменилось? Нет. Теперь сделайте следующие изменения:

Person = function () {
    this.favoriteColor = 'black';
}

Student = function () {
    Person.call(this);
    this.favoriteColor = 'blue';
}

и в конце тестового кода ...

alert(student1.favoriteColor);

Цвет будет синим.

Изменение в prototype.constructor, по моему опыту, мало что делает, если вы не делаете очень конкретные, очень сложные вещи, которые, вероятно, не являются хорошей практикой:)

Редактировать: После того, как несколько раз выкарабкались по сети и делали некоторые эксперименты, похоже, что люди устанавливают конструктор так, что он «выглядит» как вещь, которая строится с помощью «нового». Думаю, я бы сказал, что проблема в том, что javascript - это прототип языка - нет такой вещи, как наследование. Но большинство программистов исходят из программирования, который накладывает наследование как «путь». Поэтому мы придумываем всевозможные вещи, чтобы попытаться сделать этот прототип языка «классическим» языком, например, расширением «классов». Действительно, в примере, который они дали, новый студент - это человек - он не «распространяется» от другого ученика. Студент - это все о человеке, и независимо от того, кем является ученик, также. Расширьте учащегося, и все, что вы расширили, - это студент в глубине души, но настроен в соответствии с вашими потребностями.

Крокфорд немного сумасшедший и переусердствующий, но немного почитал некоторые вещи, которые он написал .. это заставит вас взглянуть на этот материал совсем по-другому.

9
ответ дан Stephen 19 August 2018 в 17:02
поделиться
  • 1
    Вау, такой простой способ достичь концепции наследования. Мне нравится тот факт, что он сохраняет свойство конструктора привязанным к функции-конструктору, которое я нашел не всегда, когда применяю другие методы. jsfiddle.net/2rmnce3g – Trace 31 July 2015 в 12:15
  • 2
    Это не наследует цепочку прототипов. – Cypher 25 September 2015 в 16:04
  • 3
  • 4
    Вам не хватает кода, который наследует прототип. Добро пожаловать в Интернет. – Cypher 28 September 2015 в 17:01
  • 5
    Фрагмент кода @Cypher был основан на коде в связанной статье. Добро пожаловать на то, чтобы прочитать вопрос целиком. Ой. Подождите. – Stephen 28 September 2015 в 20:56
  • 6
    @macher Я имел в виду это как классическое наследование. Плохой выбор формулировок с моей стороны. – Stephen 15 October 2015 в 18:47

Выполняет ли это какую-либо важную цель?

Да и нет.

В ES5 и ранее JavaScript сам не использовал constructor для что-нибудь. Он определил, что объект по умолчанию для свойства функции prototype будет иметь его и что он будет ссылаться на функцию, а - на .

Это изменилось в ES2015 (ES6), который начал использовать его по отношению к иерархиям наследования. Например, Promise#then использует свойство constructor обещанного вами обещания (через SpeciesConstructor ) при создании нового обещания для возврата. Он также участвует в подтипировании массивов (через ArraySpeciesCreate ).

Вне самого языка иногда люди использовали его при попытке создания общих функций «клонировать» или просто вообще, когда они хотел ссылаться на то, что, по их мнению, будет функцией конструктора объекта. Мой опыт в том, что использование его редко, но иногда люди его используют.

Можно ли его опустить?

Это по умолчанию, вы нужно вернуть его, когда вы замените объект на свойство функции prototype:

Student.prototype = Object.create(Person.prototype);

Если вы этого не сделаете:

Student.prototype.constructor = Student;

... тогда Student.prototype.constructor наследует от Person.prototype, который (предположительно) имеет constructor = Person. Так что это вводит в заблуждение. И, конечно, если вы подклассифицируете что-то, что использует его (например, Promise или Array), а не используя class ¹ (который обрабатывает это для вас), вам нужно убедиться, что вы правильно настроили его. Итак, в основном: это хорошая идея.

Все в порядке, если ничто в коде (или используемом вами библиотечном коде) не использует его. Я всегда гарантировал, что он правильно подключен.

Конечно, с ключевым словом ES2015 (иначе ES6) class, большую часть времени мы бы его использовали, нам не нужно потому что он обрабатывается для нас, когда мы делаем

class Student extends Person {
}

¹ "... если вы подклассифицируете что-то, что его использует (например, Promise или Array), и не используя class ... " & nbsp; & mdash; Это возможно сделать это, но это настоящая боль (и немного глупо). Вы должны использовать Reflect.construct .

63
ответ дан T.J. Crowder 19 August 2018 в 17:02
поделиться

Не нужно использовать «классы классов» или «Новый» в наши дни. Используйте литералы объектов.

Прототип объекта уже является «классом». Когда вы определяете литерал объекта, он уже является экземпляром прототипа Object. Они также могут выступать в качестве прототипа другого объекта и т. Д.

const Person = {
  name: '[Person.name]',
  greeting: function() {
    console.log( `My name is ${ this.name || '[Name not assigned]' }` );
  }
};
// Person.greeting = function() {...} // or define outside the obj if you must

// Object.create version
const john = Object.create( Person );
john.name = 'John';
console.log( john.name ); // John
john.greeting(); // My name is John 
// Define new greeting method
john.greeting = function() {
    console.log( `Hi, my name is ${ this.name }` )
};
john.greeting(); // Hi, my name is John

// Object.assign version
const jane = Object.assign( Person, { name: 'Jane' } );
console.log( jane.name ); // Jane
// Original greeting
jane.greeting(); // My name is Jane 

// Original Person obj is unaffected
console.log( Person.name ); // [Person.name]
console.log( Person.greeting() ); // My name is [Person.name]

Это стоит прочитать :

Объектно-ориентированное на основе класса Языки, такие как Java и C ++, основаны на концепции двух разных сущностей: классов и экземпляров.

...

Язык, на основе прототипа, такой как JavaScript, не делайте этого различия: у него просто есть объекты. Язык, основанный на прототипе, имеет понятие прототипического объекта, объект, используемый в качестве шаблона, из которого можно получить начальные свойства для нового объекта. Любой объект может определять свои собственные свойства либо при его создании, либо во время выполнения. Кроме того, любой объект может быть связан как прототип для другого объекта, позволяя второму объекту совместно использовать свойства первого объекта

1
ответ дан ucsarge 19 August 2018 в 17:02
поделиться

Получил хороший пример кода, почему действительно необходимо установить конструктор прототипов ..

function CarFactory(name){ 
   this.name=name;  
} 
CarFactory.prototype.CreateNewCar = function(){ 
    return new this.constructor("New Car "+ this.name); 
} 
CarFactory.prototype.toString=function(){ 
    return 'Car Factory ' + this.name;
} 

AudiFactory.prototype = new CarFactory();      // Here's where the inheritance occurs 
AudiFactory.prototype.constructor=AudiFactory;       // Otherwise instances of Audi would have a constructor of Car 

function AudiFactory(name){ 
    this.name=name;
} 

AudiFactory.prototype.toString=function(){ 
    return 'Audi Factory ' + this.name;
} 

var myAudiFactory = new AudiFactory('');
  alert('Hay your new ' + myAudiFactory + ' is ready.. Start Producing new audi cars !!! ');            

var newCar =  myAudiFactory.CreateNewCar(); // calls a method inherited from CarFactory 
alert(newCar); 

/*
Without resetting prototype constructor back to instance, new cars will not come from New Audi factory, Instead it will come from car factory ( base class )..   Dont we want our new car from Audi factory ???? 
*/
2
ответ дан user3877965 19 August 2018 в 17:02
поделиться
  • 1
    Ваш метод createNewCar создает фабрики !? Также похоже, что он должен был использоваться как var audiFactory = new CarFactory("Audi"), а не использовать наследование. – Bergi 15 June 2015 в 02:17
  • 2
    Ваш пример использует this.constructor внутри, поэтому неудивительно, что он должен быть установлен. У вас есть какой-либо пример без него? – Dmitri Zaitsev 23 May 2016 в 11:53
Другие вопросы по тегам:

Похожие вопросы: