Наследование JavaScript: когда конструктор имеет аргументы
Используя чистый JavaScript для наследования, я обычно это делаю:
function A() {}
A.prototype.run = function () {};
function B() {}
B.prototype = new A;
B.prototype.constructor = B;
Поскольку в конструктор нет аргументов, новый A не имеет права жаловаться. Теперь я не нашел способ сделать наследование, если у конструктора есть аргументы. Например,
function A(x, y) {}
A.prototype.run = function () {};
function B(x, y) {}
B.prototype = new A;
B.prototype.constructor = B;
Я мог бы передать некоторые произвольные значения, например:
B.prototype = new A(null, null);
В некоторых случаях мне может потребоваться проверить x и y в конструкторе A. В некоторых крайних случаях мне нужно бросать ошибки при проверке x или y. Тогда нет способа, чтобы B наследовал от A, используя новый A.
Любые предложения?
Спасибо!
Ответы
Ответ 1
Ну, если вы хотите сделать B.prototype
объект, который наследует от A.prototype
, не выполняя конструктор A
, чтобы избежать всех возможных побочных эффектов, вы можете использовать конструктор фиктивных объектов, чтобы сделать это, например
function tmp() {}
tmp.prototype = A.prototype;
B.prototype = new tmp();
B.prototype.constructor = B;
Вы можете создать функцию для инкапсуляции логики создания этого нового объекта, например:
function inherit(o) {
function F() {}; // Dummy constructor
F.prototype = o;
return new F();
}
//...
B.prototype = inherit(A.prototype);
B.prototype.constructor = B;
Если вы нацеливаете современные браузеры, вы можете использовать метод ECMAScript 5 Object.create
для той же цели, например:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
//..
Ответ 2
Проблема заключается в том, что вы не можете легко создать объект-прототип для B
, так как вызов конструктора A
не может быть выполнен. Это связано с тем, что параметры для неизвестного конструктора до выполнения new B
выполняются. Для создания прототипа для B
, который ссылается на прототип A
, вам нужна функция конструктора-фиктивного конструктора.
B.prototype = (function(parent){
function protoCreator(){};
protoCreator.prototype = parent.prototype;
// Construct an object linking to A.prototype without calling constructor of A
return new protoCreator();
})(A);
Как только у вас установлен объект-прототип для B
, вам нужно обеспечить вызов конструктора A
в конструкторе B
.
function B(x, y) {
// Replace arguments by an array with A arguments in case A and B differ in parameters
A.apply(this, arguments);
}
Теперь вы можете создать экземпляр B
, вызвав new B(x, y)
.
Для полного же включения проверки параметров в A
см. jsFiddle.
В исходном коде вы устанавливаете B.prototype.constructor = B
. Я не понимаю, почему вы это делаете. Свойство constructor
не влияет на иерархию наследования, для которой отвечает свойство prototype
. Если вы хотите иметь именованный конструктор, содержащийся в свойстве constructor
, вам нужно немного расширить код сверху:
// Create child prototype – Without calling A
B.prototype = (function(parent, child){
function protoCreator(){
this.constructor = child.prototype.constructor
};
protoCreator.prototype = parent.prototype;
return new protoCreator();
})(A, B);
Используя первое определение B.prototype
, вы получите следующие результаты:
var b = new B(4, 6);
b.constructor // A
console.info(b instanceof A); // true
console.info(b instanceof B); // true
В расширенной версии вы получите:
var b = new B(4, 6);
b.constructor // B
console.info(b instanceof A); // true
console.info(b instanceof B); // true
Причиной для другого вывода является то, что instanceof
следует всей цепочке прототипов B
и пытается найти подходящий объект-прототип для A.prototype
или B.prototype
(в другом вызове). Прототип b.constructor
ссылается на функцию, которая использовалась для определения прототипа экземпляров. Если вы задаетесь вопросом, почему он не указывает на protoCreator
, это связано с тем, что его прототип был перезаписан A.prototype
во время создания B.prototype
. Расширенное определение, показанное в обновленном примере, исправляет это свойство constructor
, чтобы указать на более подходящую (поскольку, вероятно, более ожидаемую) функцию.
Для ежедневного использования я бы рекомендовал отказаться от идеи использования свойства constructor
экземпляров целиком. Вместо этого используйте instanceof
, поскольку его результаты более предсказуемы/ожидаются.
Ответ 3
Рассмотрим это:
function B( x, y ) {
var b = Object.create( new A( x, y ) );
// augment b with properties or methods if you want to
return b;
}
И затем
var b = new B( 12, 13 );
Теперь b
наследуется от экземпляра A
, который, в свою очередь, наследует от A.prototype
.
Live demo: http://jsfiddle.net/BfFkU/
Object.create
не реализован в IE8, но его можно легко выполнить вручную:
if ( !Object.create ) {
Object.create = function ( o ) {
function F() {}
F.prototype = o;
return new F();
};
}
Это может быть помещено внутри файла ie8.js
, который загружается только для IE8 и ниже через условные комментарии.
Ответ 4
Хотя это старая тема, я думал, что все равно ответю.
Два способа сделать это:
Хотя способ Pseudo Classical наиболее популярен, он имеет свои нижние стороны, так как ему нужно вызвать родительский конструктор один раз в дочернем конструкторе и один раз при наследовании прототипа. Кроме того, дочерний прототип будет содержать все свойства родительского конструктора, которые в любом случае будут перезаписаны при вызове дочернего конструктора. Мой личный выбор Прототипное наследование.
1. Псевдоклассическое наследование:
function A(x, y) {}
A.prototype.run = function () {};
function B(x, y) {
A.call(this,x,y);
}
B.prototype = new A();
B.prototype.constructor = B;
2. Прототипное наследование:
function A(x, y) {}
A.prototype.run = function () {};
function B(x, y) {
A.call(this,x,y);
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;