Правильный способ наследования

Я читал исходный код RxJS4 и наткнулся на функцию, которая выполняет наследование ( https://github.com/Reactive-Extensions/RxJS/blob/master/src/core/internal/util.js):

var inherits = Rx.internals.inherits = function (child, parent) {
  function __() { this.constructor = child; }
  __.prototype = parent.prototype;
  child.prototype = new __();
};

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

2 ответа

Но я также заметил, что если я создаю родительский объект, его конструктор, его конструктор будет связан с дочерней функцией.

Это не правильно. Цепочка прототипов идет в одном направлении, а родительский элемент никак не изменяется.

Единственное предостережение в этой реализации - то, что конструктор родителя не вызывается при вызове конструктора потомка. Реализация класса ES6 обеспечивает это.

На вашей диаграмме есть пара вопросов:

  1. Пример __ не имеет свойства конструктора, указывающего на __ функция. На самом деле самая причина __ Существует для установки свойства конструктора для дочернего конструктора.

  2. __proto__ дочернего экземпляра будет экземпляром __ (Обратите внимание new __() в коде наследует) и __proto__ из этого будет parent.prototype,

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

Может быть полезно сравнить это с тем, как babel реализует семантику класса ES6:

function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

Он использует тот факт, что Object.create занимает секунду propertiesObject аргумент, который может быть использован для добавления дополнительного свойства, которое в этом случае оказывается constructor, Объект, возвращаемый Object.create служит той же цели, что и __ экземпляр возвращен new __() в коде RxJS.

Объекты и Конструкторы

В javascript нужно привыкнуть к тому, что конструктор - это класс. В то время как ES6 действительно представил class Ключевое слово и синтаксис класса это просто синтаксический сахар базового механизма. Архитектура не изменилась.

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

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

Person.prototype = {};

Person.prototype.name = "";
Person.prototype.age = null;
Person.prototype.gender = null;

Обратите внимание на несколько вещей. Это в основном перевернуто по сравнению с другими ОО языками. В других языках вы определяете класс, а затем можете определить конструктор как специальное свойство / метод класса. В javascript вы определяете конструктор, а затем определяете класс (прототип) как специальное свойство этого конструктора.

Во-вторых, здесь нет особенных constructor ключевое слово. Конструктор - это просто обычная функция. В этом нет ничего особенного. Он становится конструктором, только если вы вызываете его, используя new ключевое слово:

var x = Person(); // regular function, x is undefined
var y = new Person(); // constructor, y is an instance of Person

Таким образом, поскольку ничто не может сказать программисту, что функция является конструктором или обычная функция, программисты javascript разработали соглашение, в котором имена функций всегда начинаются с нижнего регистра, а имена конструкторов всегда начинаются с верхнего регистра. Из приведенного выше кода вы видите функцию с именем Person так что вы можете предположить, что я собираюсь сделать это конструктором.

наследование

Поскольку прототип - это, ну... прототип объекта, то для наследования от конструктора вы устанавливаете дочерний прототип в экземпляр прототипа Parent. В современном JS вы бы сделали это:

function Employee (name, job) {
    this.name = name;
    this.job = job;
}

Employee.prototype = Object.create(Person.prototype); // Inherit!

Employee.prototype.job = null;

Обратите внимание, что мы наследуем от объекта (прототипа), потому что в javascript вы наследуете от объектов, а не конструкторов и не классов.

Во-вторых, обратите внимание, что мы наследуем, устанавливая наш прототип в копию прототипа нашего родителя. Это потому, что если мы просто назначим прототип нашего родителя нашему собственному, то когда мы добавим к нему новые свойства (например, job в этом примере) мы не хотим изменять прототип нашего родителя (потому что тогда это не будет наследованием).

За несколько дней до Object.create функция существует, вы бы вместо этого сделали это:

Employee.prototype = new Person();

Это все еще актуально сегодня, хотя Object.create как правило, является предпочтительным. Итак, в коде RxJS, когда вы видите это:

child.prototype = new __();

Вот где происходит наследование. Помните, что прототип - это то, что конструктор использует в качестве шаблона для создания нового объекта. Итак, строка выше:

__.prototype = parent.prototype;

Значит у нас теперь есть функция __ который создаст объект, похожий на объект, который создал бы родитель. Так делаю new __() создаст объект, похожий на вызов родительского конструктора, но без выполнения какой-либо логики, определенной в родительском конструкторе. Так что в основном это делает что-то похожее на Object.create(parent);

Наследование - это просто присвоение копии прототипа родителя нашему собственному прототипу. Все остальные сложные биты, приведенные выше, просто готовятся к копированию прототипа родителя.

Другие вопросы по тегам