| Беларусь, рогачёв |
Эмулируем множественное наследование
Код вспомогательных функций
Итак, пора заканчивать разговоры и разбирать код. Далее приведены подробно откомментированные функции, с помощью которых реализуется множественное наследование. Начинаем с функции, которая реализует копирование. Сделаем сразу функцию, которая может работать в двух режимах: с копированием скрытых полей и методов и без него. (Для множественного наследования мы будет использовать первый из них.) Вот код этой функции.
/*=======================================================*
* Функция copyObjectExt - вспомогательная функция для копирования *
*=======================================================*/
// Главная особенность функции copyObjectExt - флаг copyWithHidden.
// Если он установлен в false, то копируются все поля и методы,
// кроме скрытых.
// Если же установить этот флаг в true, то копируются и скрытые
// зметоды, а исключением __proto__, constructor и
// __constructor__, которые мы устанавливаем вручную. После
// копирования те методы, что ранее были скрыты от for...in,
// скрываются обратно.
_global.copyObjectExt = function(sourceObj, destObj, copyWithHidden){
// Если объект, в который нужно все копировать,
// не задан, он создается.
if (destObj == null) destObj = new Object();
if (!copyWithHidden){
// Копируем нерекурсивно - вложенные объекты в прототипах,
// как правило, не используются. Если они вам нужны -
// поставьте здесь рекурсивное копирование.
// Мы, правда, далее заводим поля у обектов-функций,
// выполняющих роль конструктора. Но конструкторы мы здесь
// не копируем вовсе.
for (var name in sourceObj){
// Если защита снята заранее, все поля будут видны,
// но копировать надо не все.
if (name != "__proto__" &&
name != "constructor" &&
name != "__constructor__") destObj[name] = sourceObj[name];
}
// В некоторых случаях при отладке будет полезно узнать,
// что рассматриваемый объект - копия.
if (DEBUG) destObj["isACopy"] = "THAT'S A COPY";
}
else{
// Копируем сначала открытые поля, чтобы запомнить,
// какие были открыты.
var tempObj = copyObjectExt(sourceObj, null, false);
// Снимаем защиту со всех полей
ASSetPropFlags(sourceObj, null, 0, 1);
// Копируем все поля
copyObjectExt(sourceObj, destObj, false);
// Ставим защиту обратно: сначал на все поля вообще
ASSetPropFlags(sourceObj, null, 1);
ASSetPropFlags(destObj, null, 1);
// Потом открываем те поля, что должны быть открыты
for (var name in tempObj){
ASSetPropFlags(sourceObj, name, 0, 1);
ASSetPropFlags(destObj, name, 0, 1);
}
}
return destObj;
}Для того чтобы посмотреть, что получается в результате работы этой функции копирования, нам придется усовершенствовать функцию dumpObj, которой мы пользовались раньше. В новом варианте рядом с именем функции (поля) будет выводиться комментарий о том, что она является скрытой (если это действительно так). Если функция скрытой не является, комментария не будет никакого. Код новой функции dumpObj таков:
// Печатаем имя и значение поля, при этом предпринимаем усилия,
// чтобы null и undefined преобразовывались в "\null" и
// "\undefined", а не в пустую строку.
_global.printField = function(name, value, comment){
if (value === undefined) value = "\\undefined";
if (value === null) value = "\\null";
trace(name + " " + comment + ": " + value);
}
// Печатаем все, что доступно функции for...in.
// Если передана информация об открытых и неопределенных полях
// (то есть tempObj и undefinedList), используем ее, добавляя,
// когда нужно, комментарий "<hidden>" к имени поля.
_global.doPrintFields =
function(obj, str, tempProto, tempObj, undefinedList)
{
trace("::::::::::: " + str + " ::::::::::::");
trace("::::::::::::::::::::::::::::::::::::::::::::::::");
for (var name in obj){
// Принимаем меры для того, чтобы наши действия
// с обнулением __proto__ не отражались на выводимой
// информации.
var comment = "";
if (tempObj != null){
if (
tempObj[name + "_notInObjectSuffix"] === undefined
&& !undefinedList[name + "_notInObjectSuffix"]
){
comment = "<hidden>";
}
}
if (name == "__proto__") printField(name, tempProto,
comment);
else printField(name, obj[name], comment);
}
trace("::::::::::::::::::::::::::::::::::::::::::::::::");
}
// Чтобы увидеть скрытые поля, снимаем с них защиту.
// Попутно запоминаем, какие поля были открытыми и
// в каких было записано undefined, хотя они и существовали
_global.printFieldsByForIn = function(obj, str, tempProto, dontRecoverHidden){
if (dontRecoverHidden){
doPrintFields(obj, str, tempProto);
}
else{
// Копируем сначала открытые поля, чтобы запомнить,
// какие были открыты
var tempObj = new Object();
var undefinedList = new Object();
var tempWOSuffixes = new Object();
for (var name in obj){
// Добавляем суффиксы, чтобы не перепутать с функциями
// Object, которые в tempObj, конечно, есть.
// В отличие от случая с функцией копирования,
// пренебрегать этим нельзя, потому что до Object'а мы
// все равно доберемся, причем именно когда он будет
// в "незащищенном" состоянии.
tempObj[name + "_notInObjectSuffix"] = obj[name];
tempWOSuffixes[name] = obj[name];
if (obj[name] === undefined)
undefinedList[name + "_notInObjectSuffix"] = true;
}
// Снимаем защиту со всех полей
ASSetPropFlags(obj, null, 0, 1);
// Выводим содержимое полей
doPrintFields(obj, str, tempProto, tempObj, undefinedList);
// Ставим защиту обратно: сначалa на все поля вообще
ASSetPropFlags(obj, null, 1);
// Потом открываем те поля, что должны быть открыты
for (var name in tempWOSuffixes){
if (
tempObj[name + "_notInObjectSuffix"] !== undefined
|| undefinedList[name + "_notInObjectSuffix"]
){
ASSetPropFlags(obj, name, 0, 1);
}
}
}
}
// В этой рекурсивной функции мы используем фокус с
// "отцеплением цепочки" __proto__ - иначе оператором
// for...in были бы выведены вперемешку поля и методы от
// разных классов цепочки.
_global.printAllFields = function(obj, name, dontRecoverHidden){
var tempProto = obj.__proto__;
obj.__proto__ = null;
printFieldsByForIn(obj, name, tempProto, dontRecoverHidden);
obj.__proto__ = tempProto;
// Проверка на null не нужна: null == undefined, хотя
// отличить их и можно при помощи оператора ===.
if (obj.__proto__ != undefined)
printAllFields(obj.__proto__, name + ".__proto__");
}
// А эта функция просто вызывает основную рабочую функцию и
// добавляет "элементы оформления" (в текстовом виде, разумеется).
_global.dumpObj = function(obj, name, dontRecoverHidden){
trace("=============================================");
if (name == undefined) name = "<Dumped object>";
printAllFields(obj, name, dontRecoverHidden);
trace("=============================================");
trace("");
}
8.1.
Теперь сделаем небольшой тестовый пример. Для удобства этот и два предыдущих фрагмента кода можно разместить в трех последовательных ключевых кадрах нового флэш-ролика, только не забудьте в четвертом кадре поставить stop().
// cn1 - означает "constructor number 1".
// Похожие классы (в том числе с другими номерами)
// мы будем использовать далее в тестовых целях.
cn1 = function(a, b){
super(a + "_cn1", b + "_cn1");
trace("constr cn1: " + a + " | " + b);
}
cn1.prototype = new Array();
cn1.prototype.cn1_f = function(){trace("func: cn1_f");}
cn1.prototype.cn1_g = function(){trace("func: cn1_g");}
// Имя означает "object number 1".
on1 = new cn1(5, 6);
on1_copy = copyObjectExt(on1, null, true);
trace("\n Строчка, расположенная выше - это результат работы
конструктора");
trace("\n А теперь выведем поля и методы интересующих нас
объектов. \n");
dumpObj(on1, "on1");
dumpObj(on1_copy, "on1_copy");
trace("\n Проверяем, что мы не испортили скрытие полей и
методов. \n");
dumpObj(on1, "on1");
trace("\n А теперь раскроем все методы класса Array. \n");
ASSetPropFlags(Array.prototype, null, 0, 7);
on1_copy2 = copyObjectExt(on1, null, true);
dumpObj(on1_copy2, "on1_copy2");
dumpObj(Array.prototype, "Array.prototype");Запуск этого примера дает следующий результат:
constr cn1: 5 | 6 Строчка, расположенная выше - это результат работы конструктора А теперь выведем поля и методы интересующих нас объектов. ========================================================== ::::::::::: on1 :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 1 : 6_cn1 0 : 5_cn1 length <hidden>: 2 __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: on1.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cn1_g : [type Function] cn1_f : [type Function] length <hidden>: 0 __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: on1.__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] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: on1.__proto__.__proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: __proto__ : \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] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ========================================================== ========================================================== ::::::::::: on1_copy :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: length <hidden>: 2 0 : 5_cn1 1 : 6_cn1 cn1_f : [type Function] cn1_g : [type Function] __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: [object Object] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: on1_copy.__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] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ==========================================================
Проверяем, что мы не испортили скрытие полей и методов.
========================================================== ::::::::::: on1 :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 1 : 6_cn1 0 : 5_cn1 length <hidden>: 2 __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: on1.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: cn1_g : [type Function] cn1_f : [type Function] length <hidden>: 0 __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: on1.__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] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: on1.__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] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ==========================================================
А теперь раскроем все методы класса Array.
========================================================== ::::::::::: on1_copy2 :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: length <hidden>: 2 0 : 5_cn1 1 : 6_cn1 cn1_f : [type Function] cn1_g : [type Function] push : [type Function] pop : [type Function] concat : [type Function] shift : [type Function] unshift : [type Function] slice : [type Function] join : [type Function] splice : [type Function] toString : [type Function] sort : [type Function] reverse : [type Function] sortOn : [type Function] __constructor__ <hidden>: [type Function] constructor <hidden>: [type Function] __proto__ <hidden>: [object Object] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: on1_copy2.__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] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ========================================================== ========================================================== ::::::::::: Array.prototype :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: sortOn : [type Function] reverse : [type Function] sort : [type Function] toString : [type Function] splice : [type Function] join : [type Function] slice : [type Function] unshift : [type Function] shift : [type Function] concat : [type Function] pop : [type Function] push : [type Function] __proto__ : [object Object] constructor : [type Function] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: Array.prototype.__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] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ==========================================================
Давайте разберемся, что же мы получили. Во-первых, новая функция dumpObj работает и не портит изначально скрытые объекты. Во-вторых, функция копирования тоже работает аккуратно и копирует с учетом скрытия. В-третьих, поскольку ASSetPropFlags не действует на поля из базовых классов, скрытые функции базовых классов не копируются. Если же их раскрыть (как мы дальше поступили с классом Array ), то копируется все. Эту особенность следует учитывать при пользовании функцией копирования. Впрочем, при реализации множественного наследования мы будем ею пользоваться, предварительно "отцепляя цепочку" базовых классов путем установки __proto__ в null. Так что данная тонкость для нас не будет важна.
Код основной функции
Переходим к основной функции множественного наследования multipleInherit. Ее аргументы мы описали ранее; еще раз напомним, что в нее передаются функция-заготовка для конструктора, массив базовых классов (состоящий из субмассивов, каждый из которых содержит всю нужную информацию о конкретном базовом классе) и, если надо, массив с информацией о некопируемом системном базовом классе. Вот код этой функции.
/*=======================================================*
* Функция multipleInherit - реализует множественное наследование *
*=======================================================*/
// В функции multipleInherit надо скопировать цепочки __proto__
// для всех используемых классов. При составлении цепочки
// __proto__ нужно клонировать объекты-функции конструкторов.
_global.multipleInherit = function(constr, bases, systemBase){
// Определяем "константы" для обращения к субмассивам аргумента
// bases
var baseClassNum = 0, argsFuncNum = 1,
stopClassNum = 2, stopInclusiveNum = 3;
// Если базовых классов нет, выходим
if (bases == null || bases.length == 0) return null;
// Если systemBase не указан, то это Object
if (systemBase == null || systemBase[baseClassNum] == null){
systemBase = [Object, function (){}];
}
// Заводим локальные переменные.
// Префиксы при именах переменных означают три уровня классов:
// prev - этот класс мы уже вставили в цепочку (и к нему
// прицепляем следующие), cur - этот класс мы вставляем в
// цепочку как раз сейчас, next - этот класс мы будем вставлять
// в цепочку на следующем этапе. В свою очередь, "корень" source
// обозначает копируемую цепочку, а dest - цепочку новую.
// Поскольку мы заводим только необходимые переменные, наборы для
// исходной и для новой цепочек получились не вполне симметричны.
// Переменные для исходной цепочки
var prevSourceConstr; // Предыдущий класс (наследник текущего)
var curSourceConstr; // Текущий класс (его конструктор)
var curSourceProt; // Прототип последнего готового класса
var nextSourceProt; // Прототип следующего класса
// Переменные для новой цепочки
var prevDestProt; // Прототип последнего готового класса
var prevDestConstr; // Конструктор последнего готового класса
var curDestConstr; // Конструктор приготовляемого класса
var curDestProt; // Прототип приготовляемого класса
// Здесь мы будем держать скопированные (и модифицированные)
// базовые конструкторы
var baseConstructorsReady = new Array();
// Создаем новый класс - он будет результатом нашей работы.
// В его конструкторе будет размещена функция вызова базовых
// конструкторов. Именно этот класс и возвращает
// функция multipleInherit
var ClassToReturn = function(){
// Сохраняем ссылку на базовый конструктор, поскольку
// эту ссылку нам придется изменять.
var baseConstr = this.__proto__.__constructor__;
// Вызываем конструкторы базовых классов,
// начиная с "наиболее базовых".
// Если есть systemBase, то вызываем конструктор для него
if (systemBase != null && systemBase[baseClassNum] != null){
// Вызов делается через функцию, передающую аргументы
// (через вызов super() в ней), поэтому устанавливаем
// в качестве базового конструктора именно системный
// базовый класс.
this.__proto__.__constructor__ = systemBase[baseClassNum];
// Собственно вызов конструктора вышеописанным способом.
systemBase[argsFuncNum].apply(this, arguments);
}
// Далее вызываем конструкторы остальных базовых классов.
// Снова таки, начинаем с классов,
// ближайших к Object (или systemBase)
for (var i=bases.length - 1; i>=0; i-){
// Вызываем конструктор
baseConstructorsReady[i].apply(
this,
bases[i][argsFuncNum].apply(this, arguments)
);
}
// Восстанавливаем ссылку на конструктор базового класса
this.__proto__.__constructor__ = baseConstr;
// И потом вызываем исходный конструктор
constr.apply(this, arguments);
}
// Цепляем массивы с исходными функциями и классами
// к создаваемому классу. Это позволит нам в дальнейшем
// создать удобные утилиты.
// Для той же цели цепляем и исходную функцию-конструктор
ClassToReturn.bases = bases;
ClassToReturn.constr = constr;
// Готовимся копировать цепочки __proto__
ClassToReturn.prototype = new Object();
prevDestProt = ClassToReturn.prototype;
prevSourceConstr = null;
prevDestConstr = ClassToReturn;
ClassToReturn.constr.prototype = prevDestProt;
// Этот флаг позволит настроить цепочку классов так, чтобы
// все вызовы первых конструкторов в каждой субцепочке
// (для каждого из непосредственных базовых классов)
// производить исключительно из только что созданного нами
// конструктора класса-наследника
var isFirstInSubChain = true;
for(var i=0; i<bases.length; i++){
curSourceConstr = bases[i][baseClassNum];
curSourceProt = curSourceConstr.prototype;
isFirstInSubChain = true;
// Цикл для одной субцепочки (то есть для цепочки
// __proto__ одного из базовых классов)
while(curSourceProt){
// Проверяем, не надо ли оборвать цепочку
if(
!bases[i][stopInclusiveNum] &&
bases[i][stopClassNum] == curSourceConstr
) break;
if(
bases[i][stopInclusiveNum] &&
bases[i][stopClassNum] == prevSourceConstr
) break;
if (
curSourceConstr == systemBase[baseClassNum] ||
curSourceConstr == Object
) break;
// В системных классах constructor прототипа
// указывает на сам класс (в отличие от классов,
// сделанных самостоятельно). Проверяем этот вариант:
if (
curSourceConstr.prototype.constructor ==
curSourceConstr
&&
(
curSourceProt.constructor ==
systemBase[baseClassNum] ||
curSourceProt.constructor == Object
)
) break;
// Заводим новый объект и копируем в него функции из
// текущего прототипа исходной цепочки
curDestProt = new Object();
// При этом нам надо временно отцепить "хвост", чтобы
// не копировать его содержимое
nextSourceProt = curSourceProt.__proto__;
curSourceProt.__proto__ = undefined;
// Копируем, учитывая скрытые поля (если есть)
copyObjectExt(curSourceProt, curDestProt, true);
// Восстанавливаем исходную цепочку
curSourceProt.__proto__ = nextSourceProt;
// Теперь надо приготовить конструктор новому классу
curDestConstr = function(){
// Вызываем исходный конструктор.
// Он будет прикреплен к данному объекту-функции.
// Только перед его вызовом надо временно изменить
// ссылку на базовый конструктор в this - чтобы
// она указывала на класс, базовый к вызываемому
// конструктору, а не к классу, объектом которого
// является this
this.__proto__.__constructor__ =
arguments.callee.__constructor__;
arguments.callee.curSourceConstr.apply(this,
arguments);
// Значение this.__proto__.__constructor__ будет
// восстановлено далее в конструкторе this
}
// Теперь прикрепляем к созданному объекту-функции
// прототип и исходный конструктор
curDestConstr.prototype = curDestProt;
curDestConstr.curSourceConstr = curSourceConstr;
// Готовый конструктор цепляем к предыдущему прототипу
prevDestProt.constructor = curDestConstr;
// Его же (конструктор) надо прикрепить в качестве
// базового конструктора к производному классу
// (если только мы не в начале субцепочки; а если в
// начале, то вместо этого записываем в массив
// сгенерированный конструктор). Потом (перед вызовом)
// прикрепленный к конструктору производного класса
// базовый конструктор мы достанем,
// используя конструкцию argumetns.callee
if (!isFirstInSubChain){
prevDestConstr.__constructor__ = curDestConstr;
}
else baseConstructorsReady[i] = curDestConstr;
// Готовый объект цепляем в цепочку.
prevDestProt.__proto__ = curDestProt;
// Сдвигаемся на один шаг по цепочке
// (в направлении базовых классов)
prevSourceConstr = curSourceConstr;
curSourceConstr = curSourceProt.constructor;
// Только теперь, получив ссылку на новый исходный
// конструктор, можно менять ссылку на исходный прототип
curSourceProt = curSourceProt.__proto__;
prevDestProt = curDestProt;
prevDestConstr = curDestConstr;
// Приводим значение флага в соответствие
// с нашим новым положением в субцепочке
isFirstInSubChain = false;
} // Закончили цикл одной субцепочки
} // Закончили цикл одной серии базовых классов (виртуальных
// или нет)
// Осталось добавить базовый класс systemBase
prevDestProt.__proto__ = systemBase[baseClassNum].prototype;
// Все готово, возвращаем результат
return ClassToReturn;
}
8.2.