Исправить наследование 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).
Надеюсь, что это поможет!