Московский физико-технический институт
Опубликован: 23.12.2005 | Доступ: свободный | Студентов: 2869 / 253 | Оценка: 4.61 / 4.44 | Длительность: 27:18:00
ISBN: 978-5-9556-0051-2
Лекция 8:

Эмулируем множественное наследование

< Лекция 7 || Лекция 8: 123456 || Лекция 9 >

Код вспомогательных функций

Итак, пора заканчивать разговоры и разбирать код. Далее приведены подробно откомментированные функции, с помощью которых реализуется множественное наследование. Начинаем с функции, которая реализует копирование. Сделаем сразу функцию, которая может работать в двух режимах: с копированием скрытых полей и методов и без него. (Для множественного наследования мы будет использовать первый из них.) Вот код этой функции.

/*=======================================================*
* Функция 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.
< Лекция 7 || Лекция 8: 123456 || Лекция 9 >