Исправить наследование JavaScript

Я читал много статей о "наследовании" в javascript. Некоторые из них используют new, а другие рекомендуют Object.Create. Чем больше я читаю, тем больше сбивает с толку, потому что кажется, что существует бесконечное количество вариантов решения наследования.

Может ли кто-нибудь быть добрым, чтобы показать мне наиболее приемлемый способ (или стандартный стандарт, если он есть)?

(Я хочу иметь базовый объект Model, который я могу расширить RestModel или LocalStorageModel.)

Ответ 1

Простой: Object.create не поддерживается во всех средах, но может быть отрегулирован с помощью new. Кроме того, у них разные цели: Object.create просто создает объект, наследующий от другого, а new также вызывает функцию-конструктор. Используйте то, что подходит.

В вашем случае вам кажется, что RestModel.prototype наследуется от Model.prototype. Object.create (или его прокладка) является правильным способом, потому что вы не хотите: a) создать новый экземпляр (создать экземпляр new Model) и b) не хотите вызывать конструктор Model:

RestModel.prototype = Object.create(Model.prototype);

Если вы хотите вызвать конструктор Model в RestModels, это не имеет ничего общего с прототипами. Используйте call() или apply() для этого:

function RestModel() {
    Model.call(this); // apply Model constructor on the new object
    ...
}

Ответ 2

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

Таким образом, вы можете увидеть множество библиотек, которые пытаются использовать парадигму языка на основе класса в JS.

Кроме того, сам JS не хватает некоторых черт, которые наследуют, а также основанные на прототипах, болезненные. Например, перед Object.create не было способа создать объект из другого объекта (как должен использовать язык на основе прототипа ООП), но только с помощью конструктора function as.

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

Итак, ваше замешательство нормальное. Скажем, что "официальным" способом является использование свойства prototype, function в качестве конструктора и Object.create для создания из объекта insteance. Однако, как сказано выше, в некоторых случаях код является подробным или он не имеет функциональности, поэтому эта процедура часто обертывается каким-то образом, а затем становится вопросом личного вкуса.

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

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

function Model() {
    this.foo = "foo";
    this.array = [];
}

Model.prototype.foo = "";
Model.prototype.array = null;
Model.prototype.doNothing = function() {};

function RestModel() {
    this.bar = "bar";
}

RestModel.prototype = new Model;
RestModel.prototype.bar = ""

var myModel = new RestModel();

Теперь, здесь проблемы:

  • Конструктор Model вызывается один раз, только когда установлен RestModel.prototype. Следовательно, свойство foo для каждого экземпляра RestModel будет "foo".
  • Не только это, но все экземпляры RestModel будут иметь один и тот же экземпляр того же массива в свойстве this.array. Если вы создаете экземпляр Model напрямую, у вас есть новый экземпляр массива для каждого экземпляра.
  • myModel.constructor Model вместо RestModel. Это потому, что мы переопределяем prototype с новым экземпляром Model, который содержит constructor равен Model.
  • Если вы хотите иметь новый экземпляр RestModel с ленивым вызовом конструктора, это невозможно.

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

function Model() {
    this.foo = "foo";
    this.array = [];
}

Model.prototype.foo = "";
Model.prototype.array = null;
Model.prototype.doNothing = function() {};

function RestModel() {
    Model.call(this);

    this.bar = "bar";
}

RestModel.prototype = Object.create(Model.prototype);
RestModel.prototype.constructor = RestModel;
RestModel.prototype.bar = ""

var myModel = new RestModel();

// for lazy constructor call
var anotherModel = Object.create(RestModel.prototype);

RestModel.call(anotherModel);

Обратите внимание, что если вы не хотите использовать prototype или function в качестве конструктора, с помощью Object.create вы можете сделать это:

var Model = {
    foo: "",
    array: null,
    doNothing: function() {}
}

var RestModel = Object.create(Model);

RestModel.bar = "";

var myModel = Object.create(RestModel);

// Assuming you have to set some default values
initialize(RestModel);

Или:

var Model = {
    foo: "",
    array: null,
    doNothing: function() {},

    init : function () {
        this.foo = "foo";
        this.array = [];
    },
}

var RestModel = Object.create(Model);

RestModel.bar = "";
RestModel.init = function () {
    Model.init.call(this);

    this.bar = "bar";
}

var myModel = Object.create(RestModel);

myModel.init();

В этом случае вы в основном имитируете конструктор. Вы также можете передать descriptor в Object.create (см. документы), но он становится более подробным. Большинство людей используют своего рода функцию или метод extend (как вы, вероятно, видели в примере Backbone).

Надеюсь, что это поможет!