Беларусь, рогачёв |
Эмулируем множественное наследование
Наследование от полученных классов
Давайте убедимся, что полученный в результате использования функции multipleInherit класс пригоден для наследования от него. Пишем следующий код:
// Создаем класс, являющийся наследником classDer3 cd4 = function(a, b, c){ super(a + "_cd4", b + "_cd4", c + "_cd4"); trace("constr cd4: " + a + " | " + b + " | " + c); } cd4.prototype = new classDer3(); cd4.prototype.cd4_f = function(){trace("func: cd4_f");} // Тестируем trace("-----------------"); od4 = new cd4("A", "B", "C"); trace("-----------------"); trace("od4 = " + od4); trace("-----------------");
Мы самым обычным образом наследовались от класса classDer3, созданного ранее с помощью multipleInherit. Запуск приведенного кода дает следующий результат:
constr cn1: _cn2 | _cn2 constr cn2: | constr cx: constr cn3: | constr cm2: constrOfDerived: | | ----------------- constr cn1: B_cd4_cn2 | B_cd4_cn2 constr cn2: B_cd4 | B_cd4 constr cx: B_cd4 constr cn3: B_cd4 | C_cd4 constr cm2: A_cd4 constrOfDerived: A_cd4 | B_cd4 | C_cd4 constr cd4: A | B | C ----------------- od4 = B_cd4,C_cd4 -----------------
Все, что выведено до первой длинной черты - это результат работы конструктора при наследовании (строчка cd4.prototype = new classDer3() ). А вот после нее напечатано то, что выводит конструктор класса cd4 при создании объекта od4. При этом правильным образом отработали конструкторы всех базовых классов (так, как они и должны работать в classDer3 ). После еще одной черты помещен результат работы оператора trace("od4 = " + od4). Мы видим, что и наследник класса classDer3 ведет себя как наследник массива.
Теперь посмотрим на результаты повторного использования multipleInherit. Добавляем ко всему предыдущему коду еще один кусок:
// Создаем новый класс cy - наследник класса cn1, // чья копия входит в цепочку __proto__ класса classDer3 cy = function(a){ super(a + "_cy", a + "_bis_cy"); trace("constr cy: " + a); } cy.prototype = new cn1(); cy.prototype.cy_f = function(){trace("func: cy_f");} // А теперь с помощью multipleInherit создаем класс classDerDer - // наследника классов cy и cd4 (последний, в свою очередь, // является наследником класса classDer3, также созданного // с помощью функции multipleInherit). classDerDer = multipleInherit( function(a, b, c){ trace("constrOfDerDer: " + a + " | " + b + " | " + c); }, [ [cy, function(){return [arguments[2]];}], [cd4, function(){return arguments;}] ], [Array, function(){super(arguments[0], arguments[1], arguments[2]);}] ); // Проверяем, что у нас получилось trace("********************************"); odd = new classDerDer("AA", "BB", "CC"); trace("-----------------"); trace("odd = " + odd); dumpObj(odd, "odd");
Запустив все это, получим в консоли следующий новый кусок:
constr cn1: | ******************************** constr cn1: BB_cd4_cn2 | BB_cd4_cn2 constr cn2: BB_cd4 | BB_cd4 constr cx: BB_cd4 constr cn3: BB_cd4 | CC_cd4 constr cm2: AA_cd4 constrOfDerived: AA_cd4 | BB_cd4 | CC_cd4 constr cd4: AA | BB | CC constr cn1: CC_cy | CC_bis_cy constr cy: CC constrOfDerDer: AA | BB | CC ----------------- odd = AA,BB,CC,BB_cd4,CC_cd4 ========================================================== ::::::::::: odd :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 4 : CC_cd4 3 : BB_cd4 2 : CC 1 : BB 0 : AA length <hidden>: 5 __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: odd.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: _cn1,_cn1 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: odd.__proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: isACopy : THAT'S A COPY length <hidden>: 2 0 : _cn1 1 : _cn1 cy_f : [type Function] __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: odd.__proto__.__proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: isACopy : THAT'S A COPY length <hidden>: 0 cn1_f : [type Function] cn1_g : [type Function] __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: , ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::: odd.__proto__.__proto__.__proto__.__proto__ ::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: isACopy : THAT'S A COPY length <hidden>: 2 0 : \undefined 1 : \undefined cd4_f : [type Function] __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::: odd.__proto__.__proto__.__proto__.__proto____proto__ :::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: isACopy : THAT'S A COPY __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: ,,, ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: odd.__proto__.__proto__.__proto__.__proto__. __proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cm2_f : [type Function] length <hidden>: 4 isACopy : THAT'S A COPY __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: _cn2_cn1,_cn2_cn1 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: odd.__proto__.__proto__.__proto__.__proto__. __proto__.__proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cn3_g : [type Function] cn3_f : [type Function] 1 : _cn2_cn1 0 : _cn2_cn1 length <hidden>: 2 isACopy : THAT'S A COPY __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: _cn2_cn1,_cn2_cn1 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: odd.__proto__.__proto__.__proto__. __proto__.__proto__.__proto__. __proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cx_f : [type Function] 1 : _cn2_cn1 0 : _cn2_cn1 length <hidden>: 2 isACopy : THAT'S A COPY __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: _cn1,_cn1 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: odd.__proto__.__proto__.__proto__. __proto__.__proto__.__proto__.__proto__. __proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cn2_g : [type Function] cn2_f : [type Function] 1 : _cn1 0 : _cn1 length <hidden>: 2 isACopy : THAT'S A COPY __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: odd.__proto__.__proto__.__proto__. __proto__.__proto__.__proto__. __proto__.__proto__.__proto__. __proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cn1_g : [type Function] cn1_f : [type Function] length <hidden>: 0 isACopy : THAT'S A COPY __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: odd.__proto__.__proto__.__proto__. __proto__.__proto__.__proto__. __proto__.__proto__.__proto__. __proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: sortOn <hidden>: [type Function] reverse <hidden>: [type Function] sort <hidden>: [type Function] toString <hidden>: [type Function] splice <hidden>: [type Function] join <hidden>: [type Function] slice <hidden>: [type Function] unshift <hidden>: [type Function] shift <hidden>: [type Function] concat <hidden>: [type Function] pop <hidden>: [type Function] push <hidden>: [type Function] __proto__ <hidden>: [object Object] constructor <hidden>: [type Function] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: odd.__proto__.__proto__.__proto__.__proto__. __proto__.__proto__.__proto__.__proto__. __proto__.__proto__.__proto__. __proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: __proto__ <hidden>: \undefined toLocaleString <hidden>: [type Function] isPropertyEnumerable <hidden>: [type Function] isPrototypeOf <hidden>: [type Function] hasOwnProperty <hidden>: [type Function] toString <hidden>: [type Function] valueOf <hidden>: [type Function] addProperty <hidden>: [type Function] unwatch <hidden>: [type Function] watch <hidden>: [type Function] constructor <hidden>: [type Function] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ==========================================================
Глядя на все это, мы увидим как вполне ожидавшиеся нами вещи, так и неожиданные. Конечно, мы ожидали получить правильную цепочку __proto__ и мы ее получили. В частности, можно проконтролировать, что в ней присутствуют две копии класса cn1 - одна попала в цепочку из класса cy, а другая - из cd4 (где, в свою очередь она взялась из classDer3, а в нем - из cn2 ). Также мы видим, что в цепочке ровно один раз присутствует класс Array, причем сам, а не его копия. А вот то, что в массиве, которым является объект odd как наследник Array, имеется пять элементов, а не три - это некоторая неожиданность, хотя и легко объяснимая. Ведь у нас конструктор Array вызван дважды - один раз из конструктора класса classDer3, а второй раз - из самого конструктора classDerDer. Вообще-то это может быть неудобно: получается, что если мы наследуемся от classDer3, мы никак не можем избавиться от первого из вызовов. И, если уж на то пошло, нас может не устраивать наличие нескольких копий одного и того же класса (возможно, с разными аргументами, переданными в конструктор) в цепочке __proto__. Короче говоря, хотелось бы иметь полноценный механизм виртуальных базовых классов и при работе с классами, полученными с multipleInherit.
Однако, есть ли здесь вообще поле для деятельности? Казалось бы, виртуальные базовые классы уже реализованы нами. Тем не менее, есть одна сложность. А именно, в цепочке __proto__ у класса, сформированного при помощи multipleInherit лежат копии базовых классов. Поэтому мы не сможем применить механизм обрыва цепочки, при обнаружении, что текущий копируемый класс равен некоему заданному. Можно было бы, конечно, ссылку на исходный класс сохранить в поле копии, но такое решение повлекло бы за собой ряд проблем. Потребовалось бы сохранять эту ссылку при дальнейшем копировании, неясно, как следовало бы в таком случае поступать с конструкторами классов-"множественных наследников" (которые не рассчитаны на то, что в их цепочке __proto__ будут что-то менять), наконец, проблемы с многократным вызовом конструкторов системных базовых классов остались бы нерешенными. Поэтому мы изберем другой путь.