Объекты не наследуют прототипированные функции

У меня есть одна функция конструктора, которая действует как суперкласс:

Bla = function(a){this.a = a;}

Я прототипирую это, чтобы включить простой метод:

Bla.prototype.f = function(){console.log("f");

А теперь новый Bla(1).f(); войдет "F" в консоли. Но, допустим, мне нужен подкласс, который наследуется от Bla:

Bla2 = function(a)
{
    this.base = Bla;
    this.base();
}

x = new Bla2(5);

Теперь, как и ожидалось, x.a дает мне 5, Но, x.f является undefined! Похоже на Bla2 не унаследовал это от Bla учебный класс! Почему это происходит и как мне это исправить?

1 ответ

Решение

Похоже на Bla2 не унаследовал это от Bla учебный класс!

Правильно. Вы ничего не сделали для подключения наследства, вы только что создали члена Bla2 называется base который является Bla пример. base не является специальным идентификатором в JavaScript.

Типичный способ настройки наследования в JavaScript выглядит следующим образом:

// The base constructor function
function Base(x) {
    // Base per-instance init
    this.x = x;
}

// An example Base function
Base.prototype.foo = function() {
    console.log("I'm Base#foo, x = " + this.x);
};

// The Derived constructor function
function Derived(x, y) {
    // Normally you need to call `Base` as it may have per-instance
    // initialization it needs to do. You need to do it such that
    // within the call, `this` refers to the current `this`, so you
    // use `Function#call` or `Function#apply` as appropriate.
    Base.call(this, x);

    // Derived per-instance init
    this.y = y;
}

// Make the Derived.prototype be a new object backed up by the
// Base.prototype.    
Derived.prototype = Object.create(Base.prototype);

// Fix up the 'constructor' property
Derived.prototype.constructor = Derived;

// Add any Derived functions
Derived.prototype.bar = function() {
    console.log("I'm Derived#bar, x = " + this.x + ", y = " + this.y);
};

...где Object.create это от ES5, но это одна из вещей, которая легко может быть в основном отодвинута. (Или вы можете использовать функцию, которая делает только минимум, не пытаясь сделать все Object.create; см. ниже.) И тогда вы используете это:

var d = new Derived(4, 2);
d.foo(); // "I'm Base#foo, x = 4"
d.bar(); // "I'm Derived#bar, x = 4, y = 2"

Живой пример | источник

В старом коде вы иногда видите Derived.prototype вместо этого настройте так:

Derived.prototype = new Base();

... но есть проблема с этим: Base может выполнить инициализацию для каждого экземпляра, которая не подходит для всей Derived наследовать. Это может даже потребовать аргументов (как наш Base делает; что бы мы сдали за x?). Вместо того, чтобы сделать Derived.prototype просто быть новым объектом, поддерживаемым Base.prototype мы получаем правильные вещи. Тогда мы называем Base изнутри Derived чтобы получить инициализацию за экземпляр.

Вышесказанное является очень простым и, как вы можете видеть, включает в себя ряд шагов. Он также мало или ничего не делает для того, чтобы сделать "супер-вызовы" простыми и легко обслуживаемыми. Вот почему вы видите так много сценариев "наследования", таких как Prototype Class, Base2 Дина Эдвардса, или (кашель) мой Lineage,


Если вы не можете полагаться на наличие функций ES5 в своей среде и не хотите включать в себя прокладку, которая выполняет основные функции Object.create Вы можете просто использовать эту функцию вместо нее:

function simpleCreate(proto) {
    function Ctor() {
    }
    ctor.prototype = proto;
    return new Ctor();
}

Тогда вместо

Derived.prototype = Object.create(Base.prototype);

вы бы сделали:

Derived.prototype = simpleCreate(Base.prototype);

Конечно, вы можете сделать больше, чтобы автоматизировать подключение - и это все Lineage в основном делает.


... и наконец: выше я использовал анонимные функции для простоты, например:

Base.prototype.foo = function() {
    // ...
};

... но я не делаю этого в своем реальном коде, потому что мне нравится помогать моим инструментам. Поэтому я склонен использовать шаблон модуля вокруг каждого "класса" (функции конструктора и связанного с ним прототипа) и использовать объявления функций (поскольку я работаю для Интернета, а в IE7 и IE8 по- прежнему возникают проблемы с выражениями именованных функций. т использование Lineage Я бы сделал выше, как это:

// Base
(function(target) {
    // Base constructor
    target.Base = Base;
    function Base(x) {
        // Base per-instance init
        this.x = x;
    }

    // An example Base function
    Base.prototype.foo = Base$foo;
    function Base$foo() {
        console.log("I'm Base#foo, x = " + this.x);
    }
})(window);

// Derived
(function(target, base) {
    // The Derived constructor function
    target.Derived = Derived;
    function Derived(x, y) {
        // Init base
        base.call(this, x);

        // Derived per-instance init
        this.y = y;
    }

    // Make the Derived.prototype be a new object backed up by the
    // Base.prototype.    
    Derived.prototype = Object.create(base.prototype);

    // Fix up the 'constructor' property
    Derived.prototype.constructor = Derived;

    // Add any Derived functions
    Derived.prototype.bar = Derived$bar;
    function Derived$bar() {
        console.log("I'm Derived#bar, x = " + this.x + ", y = " + this.y);
    }
})(window, Base);

...или что-то типа того. Живая копия | источник

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