Наследование JavaScript: вызов супер-конструктора или использование цепочки прототипов? - PullRequest
76 голосов
/ 11 ноября 2010

Совсем недавно я читал об использовании вызовов JavaScript в MDC

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call

, но в одном примере, показанном ниже, я до сих пор не понимаю.они используют наследование здесь как это

Prod_dept.prototype = new Product();

это необходимо?Потому что в любом случае есть вызов супер-конструктора в

Prod_dept()

, вот так

Product.call

это просто необычное поведение?Когда лучше использовать вызов для супер-конструктора или использовать цепочку прототипов?

function Product(name, value){
  this.name = name;
  if(value >= 1000)
    this.value = 999;
  else
    this.value = value;
}

function Prod_dept(name, value, dept){
  this.dept = dept;
  Product.call(this, name, value);
}

Prod_dept.prototype = new Product();

// since 5 is less than 1000, value is set
cheese = new Prod_dept("feta", 5, "food");

// since 5000 is above 1000, value will be 999
car = new Prod_dept("honda", 5000, "auto");

Спасибо за разъяснение

Ответы [ 3 ]

106 голосов
/ 08 декабря 2010

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

  • Установка прототипа для экземпляра родительского объекта инициализирует цепочку прототипов (наследование), это делается только один раз (так какобъект-прототип является общим).
  • Вызов конструктора родителя инициализирует сам объект, это делается при каждом создании экземпляра (вы можете передавать различные параметры каждый раз, когда создаете его).

Поэтому не следует вызывать конструктор родителя при настройке наследования.Только при создании экземпляра объекта, который наследуется от другого.

Ответ Криса Моргана почти завершен, в нем пропущена небольшая деталь (свойство конструктора).Позвольте мне предложить метод для настройки наследования.

function extend(base, sub) {
  // Avoid instantiating the base class just to setup inheritance
  // Also, do a recursive merge of two prototypes, so we don't overwrite 
  // the existing prototype, but still maintain the inheritance chain
  // Thanks to @ccnokes
  var origProto = sub.prototype;
  sub.prototype = Object.create(base.prototype);
  for (var key in origProto)  {
     sub.prototype[key] = origProto[key];
  }
  // The constructor property was set wrong, let's fix it
  Object.defineProperty(sub.prototype, 'constructor', { 
    enumerable: false, 
    value: sub 
  });
}

// Let's try this
function Animal(name) {
  this.name = name;
}

Animal.prototype = {
  sayMyName: function() {
    console.log(this.getWordsToSay() + " " + this.name);
  },
  getWordsToSay: function() {
    // Abstract
  }
}

function Dog(name) {
  // Call the parent's constructor
  Animal.call(this, name);
}

Dog.prototype = {
    getWordsToSay: function(){
      return "Ruff Ruff";
    }
}    

// Setup the prototype chain the right way
extend(Animal, Dog);

// Here is where the Dog (and Animal) constructors are called
var dog = new Dog("Lassie");
dog.sayMyName(); // Outputs Ruff Ruff Lassie
console.log(dog instanceof Animal); // true
console.log(dog.constructor); // Dog

См. Мой пост в блоге для еще большего синтаксического сахара при создании классов.http://js -bits.blogspot.com / 2010/08 / javascript-inheritance-done-right.html

Техника скопирована из Ext-JS и http://www.uselesspickles.com/class_library/ икомментарий от https://stackoverflow.com/users/1397311/ccnokes

30 голосов
/ 11 ноября 2010

Идеальный способ сделать это - , а не do Prod_dept.prototype = new Product();, потому что это вызывает конструктор Product.Таким образом, идеальный способ - клонировать его, за исключением конструктора, что-то вроде этого:

function Product(...) {
    ...
}
var tmp = function(){};
tmp.prototype = Product.prototype;

function Prod_dept(...) {
    Product.call(this, ...);
}
Prod_dept.prototype = new tmp();
Prod_dept.prototype.constructor = Prod_dept;

Тогда супер-конструктор вызывается во время построения, что вам и нужно, потому что тогда вы можете передавать параметры,тоже.

Если вы посмотрите на такие вещи, как Google Closure Library, вы увидите, как они это делают.

6 голосов
/ 22 декабря 2011

Если вы выполнили объектно-ориентированное программирование на JavaScript, вы будете знать, что вы можете создать класс следующим образом:

Person = function(id, name, age){
    this.id = id;
    this.name = name;
    this.age = age;
    alert('A new person has been accepted');
}

Пока у нашего ученика есть только два свойства, и мы собираемся дать ему несколько методов. Чистый способ сделать это использовать его объект-прототип. Начиная с JavaScript 1.1, объект-прототип был представлен в JavaScript. Это встроенный объект, который упрощает процесс добавления пользовательских свойств и методов ко всем экземплярам объекта. Давайте добавим 2 метода в наш класс, используя его объект 'prototype' следующим образом:

Person.prototype = {
    /** wake person up */
    wake_up: function() {
        alert('I am awake');
    },

    /** retrieve person's age */
    get_age: function() {
        return this.age;
    }
}

Теперь мы определили наш класс Person. Что, если мы хотим определить другой класс с именем Manager, который наследует некоторые свойства от Person. Нет смысла переопределять все эти свойства снова, когда мы определяем наш класс Manager, мы можем просто установить его наследование от класса Person. JavaScript не имеет встроенного наследования, но мы можем использовать технику для реализации наследования следующим образом:

Inheritance_Manager = {}; // Создаем класс диспетчера наследования (имя произвольное)

Теперь давайте передадим нашему классу наследования метод под названием extend, который принимает аргументы baseClass и subClassas. В рамках метода extends мы создадим внутренний класс под названием унаследованная функция inheritance () {}. Причина, почему мы используем этот внутренний Класс должен избежать путаницы между прототипами baseClass и subClass. Далее мы делаем прототип нашего класса наследования указав на прототип baseClass, как показано в следующем коде: inheritance.prototype = baseClass. прототип; Затем мы копируем прототип наследования в прототип subClass следующим образом: subClass.prototype = new inheritance (); Далее нужно указать конструктор для нашего подкласса следующим образом: subClass.prototype.constructor = subClass; Закончив прототипирование нашего подкласса, мы можем указать следующие две строки кода, чтобы установить некоторые указатели базового класса.

subClass.baseConstructor = baseClass;
subClass.superClass = baseClass.prototype;

Вот полный код нашей функции расширения:

Inheritance_Manager.extend = function(subClass, baseClass) {
    function inheritance() { }
    inheritance.prototype = baseClass.prototype;
    subClass.prototype = new inheritance();
    subClass.prototype.constructor = subClass;
    subClass.baseConstructor = baseClass;
    subClass.superClass = baseClass.prototype;
}

Теперь, когда мы реализовали наше наследование, мы можем начать использовать его для расширения наших классов. В этом случае мы собираемся расширить наш класс Person в класс Manager следующим образом:

Определяем класс менеджера

Manager = function(id, name, age, salary) {
    Person.baseConstructor.call(this, id, name, age);
    this.salary = salary;
    alert('A manager has been registered.');
}

мы заставляем его наследовать форму Person

Inheritance_Manager.extend(Manager, Person);

Если вы заметили, мы только что вызвали метод extends нашего класса Inheritance_Manager и передали в нашем случае диспетчер подкласса, а затем базовый класс. Обратите внимание, что порядок очень важен здесь. Если вы поменяете их, наследство не будет работать, как вы предполагали, если вообще. Также обратите внимание, что вам нужно будет указать это наследование, прежде чем вы сможете определить наш подкласс. Теперь давайте определим наш подкласс:

Мы можем добавить больше методов, как показано ниже. Наш класс Manager всегда будет иметь методы и свойства, определенные в классе Person, потому что он наследуется от него.

Manager.prototype.lead = function(){
   alert('I am a good leader');
}

Теперь, чтобы проверить это, давайте создадим два объекта, один из класса Person и один из унаследованного класса Manager:

var p = new Person(1, 'Joe Tester', 26);
var pm = new Manager(1, 'Joe Tester', 26, '20.000');

Не стесняйтесь, чтобы получить полный код и больше комментариев на: http://www.cyberminds.co.uk/blog/articles/how-to-implement-javascript-inheritance.aspx

...