Меня всегда немного бесило отсутствие в Javascript понятия класса как такового, а с наследованием вообще довольно туговато. Почитав на эту тему довольно много материалов и попробовав несколько «обходных» путей отсюда, в том числе и приведенную в комментариях немного урезанную версию библиотеки ajaxoop.js, не нашел ничего подходящего и привычного.
Однако была задача, потребовавшая реализации «привычного» наследования, дополнительным приятным побочным эффектом которой стало наличие возможности множественного наследования.
Итак, предлагаю такое достаточно простое на мой взгляд решение этой проблемы:
Для примера создадим 2 класса родителя:
и два класса дочерних классов, каждый из которых наследует от своего родителя:
ну и суммарный класс, наследующий от дочерних классов:
создадим объект последнего класса и проверим, что выдаст метод message() данного объекта:
А вот и результат:
Как видно, все родительские методы работают должным образом. Надеюсь, это решение будет полезно многим, удачи!
Однако была задача, потребовавшая реализации «привычного» наследования, дополнительным приятным побочным эффектом которой стало наличие возможности множественного наследования.
Итак, предлагаю такое достаточно простое на мой взгляд решение этой проблемы:
/**
* Базовый класс для наследования.
* Также позволяет реализовать множественное наследование.
* Использование:
* function MyClass(arg1, arg2) {
* ExtClass.call(this, {
* MyParentClass1: [arg1], // родительский класс 1 с параметрами
* MyParentClass2: [arg2], // родительский класс 2
* MyParentClass3: [arg1, arg2], // и так далее
* ...
* });
*
* // далее можно реализовывать собственные методы или переписывать
* // родительские
* }
*
* После такого вызова ваш класс MyClass унаследует все методы и свойства
* родительских классов MyParentClass1 и т.д.
* Если оба класса имеют методы с одинаковым именем, то MyClass
* унаследует в качестве своего метода последний из перечисленных
* в вызове родительских классов.
* Однако к любому методу любого из родительских классов всегда можно
* обратиться посредством следующей конструкции:
* this.$super['MyParentClassName'].methodName.call(this, ...args...);
* так как методы всех родителей будут занесены в this.$super
*/
function ExtClass(supers) {
// переменная для хранения методов родительских классов
// здесь будут числится не только прямые предки (родительские классы,
// перечисленные при вызове), но и их предки тоже
this.$super = this.$super ? this.$super : {};
// переменная для хранения массива имен родительских классов
// для реализации метода this.instanceOf()
this.$_parents = this.$_parents ? this.$_parents : [];
/**
* Проверка, является ли объект объектом заданного класса
* @param string className Имя класса для проверки
* @returns boolean TRUE если является, иначе FALSE
*/
this.instanceOf = function(className) {
return (__inArray(className, this.$_parents) || (this instanceof eval(className)));
};
/**
* Приватная функция для добавления родительского класса.
* Методы род. класса могут быть вызваны так:
* this.$super['parenClassName'].method.call(this, ...args...);
* @param object that Обертка для this объекта
* @param string className Имя класса
*/
function __addSuper(that, className) {
var obj = new (eval(className));
that.$super[className] = {};
if (!__inArray(className, that.$_parents)) that.$_parents.push(className);
for (i in obj) {
if (typeof obj[i] == 'function') {
that.$super[className][i] = obj[i];
}
}
};
/**
* Приватная функция для проверки на наличие элемента в масиве
* @param mixed value Искомое значение
* @param array arraySeek Массив поиска
* @returns boolean TRUE если найдено, иначе FALSE
*/
function __inArray(value, arraySeek) {
if (arraySeek && arraySeek.length) {
for (i in arraySeek) {
if (arraySeek[i] == value) return true;
}
}
return false;
};
// основной цикл добавления родительских классов
for (i in supers) {
var className = i;
var args = supers[i];
(eval(className)).apply(this, args);
__addSuper(this, className);
}
};
Для примера создадим 2 класса родителя:
function Parent1() {
ExtClass.call(this); // можно не вызывать
this.message = function() {
return 'Parent1::message';
}
}
function Parent2() {
ExtClass.call(this); // можно не вызывать
this.message = function() {
return 'Parent2::message';
}
}
и два класса дочерних классов, каждый из которых наследует от своего родителя:
function Child1() {
ExtClass.call(this, {
Parent1: null
});
this.message = function() {
return 'Child1::message';
}
}
function Child2() {
ExtClass.call(this, {
Parent2: null
});
this.message = function() {
return 'Child2::message';
}
}
ну и суммарный класс, наследующий от дочерних классов:
function Child12() {
ExtClass.call(this, {
Child1: null,
Child2: null
});
this.message = function() {
// вызов по очереди одноименного метода всех родительских классов
var message = this.$super['Parent1'].message.call(this) + "\n";
message += this.$super['Parent2'].message.call(this) + "\n";
message += this.$super['Child1'].message.call(this) + "\n";
message += this.$super['Child2'].message.call(this) + "\n";
// ну и от себя )
message += 'Child12::message'
return message;
}
}
создадим объект последнего класса и проверим, что выдаст метод message() данного объекта:
var childTest = new Child12();
alert(childTest.message());
А вот и результат:
Parent1::message
Parent2::message
Child1::message
Child2::message
Child12::message
Как видно, все родительские методы работают должным образом. Надеюсь, это решение будет полезно многим, удачи!