Объекты не наследуют прототипированные функции
У меня есть одна функция конструктора, которая действует как суперкласс:
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);
...или что-то типа того. Живая копия | источник