Подтвердить что ты не робот

Наследование 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.

Любые предложения?

Спасибо!

4b9b3361

Ответ 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;