Однако была задача, потребовавшая реализации «привычного» наследования, дополнительным приятным побочным эффектом которой стало наличие возможности множественного наследования.
Итак, предлагаю такое достаточно простое на мой взгляд решение этой проблемы:
/** * Базовый класс для наследования. * Также позволяет реализовать множественное наследование. * Использование: * 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
Как видно, все родительские методы работают должным образом. Надеюсь, это решение будет полезно многим, удачи!

