Опубликован: 23.12.2005 | Уровень: специалист | Доступ: платный | ВУЗ: Московский физико-технический институт
Лекция 8:

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

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

Тестируем основную функцию

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

Для удобства мы разместили тестовые функции в нескольких кадрах, что рекомендуем сделать и вам. Только не забудьте поставить stop(); в ключевом кадре, стоящем после всех тестовых. Итак, кадр с первой тестовой иерархией.

// Включаем отладочный режим - это повлияет на поведение 
// функции копирования, которая станет помечать копии
// специальным текстовым полем
_global.DEBUG = true;
// Заводим тестовые классы
// Имя cn1 означает class number 1 
// (или constructor number 1).
// Классы серии cn отнаследованы от массива (класса Array)
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");}
// class number 2
cn2 = function(a, b){
	super(a + "_cn2", b + "_cn2");
	trace("constr cn2: " + a + " | " + b);	
}
cn2.prototype = new cn1();
cn2.prototype.cn2_f = function(){trace("func: cn2_f");}
cn2.prototype.cn2_g = function(){trace("func: cn2_g");}
// class number 3
cn3 = function(a, b){
	super(a + "_cn3", b + "_cn3");
	trace("constr cn3: " + a + " | " + b);	
}
cn3.prototype = new cn2();
cn3.prototype.cn3_f = function(){trace("func: cn3_f");}
cn3.prototype.cn3_g = function(){trace("func: cn3_g");}
trace("====================================");
// Object of class number 3
on3 = new cn3("a~arg", "b~arg");
trace("------------------");
// Проверяем работоспособность функций
on3.cn3_g();
on3.cn1_f();
// Проверяем, что объект может работать (и печататься) как массив
trace("on3: " + on3);
// Смотрим, что у нас получился за объект - 
// пока без использования множественного наследования
dumpObj(on3, "on3");

Посмотрим, что выводят в окно Output функции из этого кадра. (Здесь множественное наследование мы еще не использовали. Пока что выясним поподробнее, что представляют собой исходные классы.)

constr cn1:  | 
constr cn1: _cn2 | _cn2
constr cn2:  | 
====================================
constr cn1: a~arg_cn3_cn2 | b~arg_cn3_cn2
constr cn2: a~arg_cn3 | b~arg_cn3
constr cn3: a~arg | b~arg
------------------
func: cn3_g
func: cn1_f
on3: a~arg_cn3_cn2_cn1,b~arg_cn3_cn2_cn1
==========================================================
:::::::::::  on3  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
1 : b~arg_cn3_cn2_cn1
0 : a~arg_cn3_cn2_cn1
length <hidden>: 2
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: _cn2_cn1,_cn2_cn1
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  on3.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
cn3_g : [type Function]
cn3_f : [type Function]
1 : _cn2_cn1
0 : _cn2_cn1
length <hidden>: 2
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: _cn1,_cn1
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  on3.__proto__.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
cn2_g : [type Function]
cn2_f : [type Function]
1 : _cn1
0 : _cn1
length <hidden>: 2
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  on3.__proto__.__proto__.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
cn1_g : [type Function]
cn1_f : [type Function]
length <hidden>: 0
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  on3.__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]
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::: on3.__proto__.__proto__.__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]
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
==========================================================

Что же мы видим? До первой двойной черты (напечатанной знаками равенства) - то, что выводят конструкторы в процессе наследования (при создании нового объекта для помещения в prototype ). Далее (под чертой) то, что печатают конструкторы (видим, что все базовые конструкторы вызваны с правильными аргументами). Потом - результат работы двух функций "на пробу" (функции печатают то, что и ожидалось). Еще дальше - выводим с помощью trace сам вновь созданный объект (поскольку он унаследован от Array, то и в самом деле, через запятую выводятся значения, переданные в конструктор Array через цепочку вызовов super() ). Наконец, еще ниже расположен результат работы функции dumpObj, из которого можно подробно узнать обо всех полях и методах получившегося объекта.

Теперь заведем еще одну иерархию классов. Она аналогична предыдущей, только происходит от класса String и короче. О ней мы уже не будем выводить столь подробные данные. Код, который ее создает, таков:

trace("\n");
// Следующую серию классов называем аналогично первой, 
// только букву n меняем на m.
// Все эти классы являются наследниками String
cm1 = function(a){
	super(a + "_cm1");
	trace("constr cm1: " + a);	
}
cm1.prototype = new String();
cm1.prototype.cm1_f = function(){trace("func: cm1_f");}
// Второй класс серии m
cm2 = function(a){
	super(a + "_cm2");
	trace("constr cm2: " + a);	
}
cm2.prototype = new cm1();
cm2.prototype.cm2_f = function(){trace("func: cm2_f");}
trace("====================================");
// Объект втогого класса серии m
om2 = new cm2("a-arg");
trace("------------------");
// Проверяем, что функции работают
om2.cm2_f();
om2.cm1_f();
// Проверяем, что объект не потерял свойства строки
trace("om2: " + om2);

Конструкторы, вызываемые функции и тестовый trace дают на выходе

constr cm1:
====================================
constr cm1: a-arg_cm2
constr cm2: a-arg
------------------
func: cm2_f
func: cm1_f
om2: a-arg_cm2_cm1

Так что все работает, как ожидалось.

А вот дальше начинается то, ради чего, собственно, и написана эта лекция. Дальше мы сделаем два класса, унаследованных от обеих иерархий, причем один совсем простой, а другой - уже с использованием стоп-классов и некопируемого (системного) базового класса. Вот этот код:

// Базовые классы готовы, начинаем применять multipleInherit.
// Простейщий вариант - без виртуальных классов
classDer1 = multipleInherit(
	function(a, b, c){
		trace("constrOfDerived: " + a + " | " + b + " | " + c);
	},
	[
		[cn3, function(){return [arguments[0], arguments[1]];}],
		[cm2, function(){return [arguments[2]];}]
	]
);
// А здесь используем системный некопируемый класс
classDer2 = multipleInherit(
	function(a, b, c){
		trace("constrOfDerived: " + a + " | " + b + " | " + c);
	},
	[
		[cm2, function(){return [arguments[0]];}, cm1],
		[cn3, function(){return [arguments[1], arguments[2]];}]
	],
	[Array, function(){super(arguments[0], arguments[1], 
arguments[2]);}]
);
trace("********************************");
// Создаем объекты. od1 означает object of "derived 1"
od1 = new classDer1("aaaa", "bbbb", "cccc");
// Проверяем, что функции работают
trace("---");
od1.cn3_g();
od1.cn1_f();
trace("---");
od1.cm2_f();
od1.cm1_f();
trace("---");
// Смотрим, как объект выводится
trace("od1 = " + od1);
trace("********************************");
// Теперь смотрим, что у получившегося объекта внутри
dumpObj(od1, "od1");
trace("********************************");
// Следующий объект создаем, соответственно, из класса derived 2.
od2 = new classDer2("aaaa", "bbbb", "cccc");
// Смотрим, как объект выводится
trace("od2 = " + od2);
// И что у него внутри
dumpObj(od2, "od2");

Основная часть этого кода (то есть вызовы multipleInherit ), как мы уже говорили, создает два класса. Первый из них - совершенно "бесхитростный" наследник классов cn3 и cm2. Конструктор его будет выводить три переданных в него аргумента, разделенных знаком " | ". В конструктор cn3 будут передаваться первые два аргумента, а в cm2 - третий аргумент.

Второй из создаваемых классов отличается от первого тремя вещами. Во-первых, порядком следования базовых классов (что влияет на порядок выполнения конструкторов - первыми выполняются конструкторы классов, указанных в самом конце, так как они стоят ближе всего к "корню" цепочки). Во-вторых, при наследовании от cm2 указан стоп-класс cm1 - это значит, что cm1 и его базовые классы не будут включены в цепочку (а сам cm2 - будет). В-третьих, класс Array указан в качестве системного некопируемого класса. При этом указано, что в его конструктор будут переданы все аргументы, с которыми будет вызван конструктор "множественного наследника".

Далее создаются объекты каждого из этих классов, у этих объектов вызывается несколько методов, а также для каждого из вновь созданных объектов вызывается функция dumpObj. В результате в консоль выводится следующая (довольно объемистая) информация.

********************************
constr cm1: cccc_cm2
constr cm2: cccc
constr cn1: aaaa_cn3_cn2 | bbbb_cn3_cn2
constr cn2: aaaa_cn3 | bbbb_cn3
constr cn3: aaaa | bbbb
constrOfDerived: aaaa | bbbb | cccc
---
func: cn3_g
func: cn1_f
---
func: cm2_f
func: cm1_f
---
od1 = 
********************************
==========================================================
:::::::::::  od1  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od1.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: _cn2_cn1,_cn2_cn1
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od1.__proto__.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
length <hidden>: 2
0 : _cn2_cn1
1 : _cn2_cn1
cn3_f : [type Function]
cn3_g : [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: _cn1,_cn1
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od1.__proto__.__proto__.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
length <hidden>: 2
0 : _cn1
1 : _cn1
cn2_f : [type Function]
cn2_g : [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::  od1.__proto__.__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>: 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::: od1.__proto__.__proto__.__proto__.__proto__.__proto__  ::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
push <hidden>: [type Function]
pop <hidden>: [type Function]
concat <hidden>: [type Function]
shift <hidden>: [type Function]
unshift <hidden>: [type Function]
slice <hidden>: [type Function]
join <hidden>: [type Function]
splice <hidden>: [type Function]
toString <hidden>: [type Function]
sort <hidden>: [type Function]
reverse <hidden>: [type Function]
sortOn <hidden>: [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: [type Object]
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
: od1.__proto__.__proto__.__proto__.__proto__.
__proto__.__proto__ :
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
length <hidden>: 4
cm2_f : [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: [type Object]
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: od1.__proto__.__proto__.__proto__.__proto__.__proto__.__
proto__.__proto__ :
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
length <hidden>: 0
cm1_f : [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: [type Object]
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::: od1.__proto__.__proto__.__proto__.__proto__.__proto__.
__proto__.__proto__.__proto__ :::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
valueOf <hidden>: [type Function]
toString <hidden>: [type Function]
toUpperCase <hidden>: [type Function]
toLowerCase <hidden>: [type Function]
charAt <hidden>: [type Function]
charCodeAt <hidden>: [type Function]
concat <hidden>: [type Function]
indexOf <hidden>: [type Function]
lastIndexOf <hidden>: [type Function]
slice <hidden>: [type Function]
substring <hidden>: [type Function]
split <hidden>: [type Function]
substr <hidden>: [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: [object Object]
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od1.__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]
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
==========================================================

********************************
constr cn1: bbbb_cn3_cn2 | cccc_cn3_cn2
constr cn2: bbbb_cn3 | cccc_cn3
constr cn3: bbbb | cccc
constr cm2: aaaa
constrOfDerived: aaaa | bbbb | cccc
od2 = aaaa,bbbb,cccc
==========================================================
:::::::::::  od2  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
2 : cccc
1 : bbbb
0 : aaaa
length <hidden>: 3
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od2.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: ,,,
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od2.__proto__.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
length <hidden>: 4
cm2_f : [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: _cn2_cn1,_cn2_cn1
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od2.__proto__.__proto__.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
length <hidden>: 2
0 : _cn2_cn1
1 : _cn2_cn1
cn3_f : [type Function]
cn3_g : [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: _cn1,_cn1
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od2.__proto__.__proto__.__proto__.__
proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
length <hidden>: 2
0 : _cn1
1 : _cn1
cn2_f : [type Function]
cn2_g : [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od2.__proto__.__proto__.__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>: 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od2.__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]
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od2.__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]
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
==========================================================

Что же мы получили на этот раз? Под первой строчкой из звездочек выведен результат работы конструктора класса classDer1. (Напомним, что конструкторы всех его базовых классов устроены так, что прибавляют суффиксы с именем класса к аргументам, передаваемым в super() ). Видно, что сначала сработала цепочка вызовов конструкторов базового класса cm2, а потом - цепочка cn3. Затем (после короткой черты) расположено то, что вывели методы объекта od1 (класса classDer1 ), унаследованные им от различных базовых классов. Результаты работы методов разных базовых классов мы нарочно разделили еще одной короткой чертой. Все методы отработали как положено.

Потом мы попробовали напечатать od1 с помощью trace и обнаружили, что наш "массив" (ведь этот класс был наследован от массива) пуст. (На самом деле, в числе базовых классов был также и класс String. Все дальнейшие рассуждения в этом абзаце относятся к нему в той же мере, что и к Array.) Массив пуст, а ведь при обычном наследовании класс cn3 вел себя не так и массив вовсе не был пустым. Все это связано с тем, что базовый класс Array был скопирован вместе с остальными и его конструктор, так же как и остальные, вызывается через apply (а не через super ). А это, как мы ранее выяснили, для системных базовых классов неприемлемо. В принципе, можно было вызывать через super абсолютно все базовые конструкторы, но это было бы не так удобно. Мы не смогли бы передавать аргументы в базовые классы в массиве. Эта возможность особенно ценна, если в базовый класс передаются в точности те же аргументы, что и в производный (что бывает очень часто), и мы можем в качестве функции, формирующей аргументы, передать просто function(){return arguments;}. Итак, если правильный вызов конструктора Array был бы важен для нас, нам следовало бы указать Array в качестве системного базового класса (что и сделано в классе classDer2 ). А пока будем считать, что для работы класса classDer1 эти тонкости несущественны, потому-то мы о них и не позаботились.

Далее идет дамп полей и методов объекта od1. Из дампа также видно, что полей с именами 0, 1 и т.д., которые должны были бы присутствовать, если бы правильно сработал конструктор Array в созданном нами объекте, нет. Но давайте посмотрим, из чего состоит цепочка __proto__ объекта od1. Объект od1.__proto__ не содержит в себе ничего интересного - это прототип, лежащий в функции-конструкторе, созданной нами при помощи multipleInherit. В него можно было бы поместить специфичные для класса classDer1 методы, но в данном тестовом примере мы этого не делали. А вот далее в цепочке мы увидим плоды нашей работы (точнее, работы функции multipleInherit ). Хотя мы и не снабдили классы отладочными полями, в которых были бы указаны их имена, разобраться, с каким именно классом мы имеем дело, можно, взглянув на имена функций - они снабжены соответствующими префиксами. Ит ак, сначала (при движении по направлению к корню цепочки) мы видим классы cn3, cn2 и cn1. Затем идет скопированный класс Array (его тоже можно определить по набору методов). Потом идут классы cm2, cm1 и скопированный класс String. Наконец, самым последним идет Object (он не скопирован, поскольку, если systemBase не указан, в качестве него подразумевается Object ).

Дальше идет информация, относящаяся уже к классу classDer2. Мы видим, что, в отличие от classDer1, его содержимое как массива соответствует тому, что мы хотели. Это следует как из результата работы trace, так и из дампа. Подробнее изучая дамп, мы видим, что правильно сработало наследование от cm2 - мы скомандовали не включать классы, начиная с cm1, так и произошло. Далее в цепочке присутствуют cn3, cn2, cn1 и Array, причем Array, в отличие от всех остальных, не скопирован (и конструктор его вызывается через super, а не через apply ).

А теперь давайте посмотрим, как с помощью multipleInherit можно реализовать виртуальные базовые классы. Собственно говоря, идея очень простая: класс, который мы хотим объявить виртуальным базовым, мы указываем в качестве стоп-класса для всех базовых классов, у которых в цепочке __proto__ он присутствует. То есть цепочки __proto__ этих классов будут оборваны именно на виртуальном базовом классе. Останется лишь указать этот класс в качестве базового (причем разместить его ближе к концу списка базовых классов - ближе, чем расположены оба его наследника). Вот код, который реализует эту идею:

// Эмулируем виртуальные базовые классы
// Для этого сначала создадим еще один класс - наследник cn2
cx = function(a){
	super(a + "_cx", a + "_bis_cx");
	trace("constr cx: " + a);	
}
cx.prototype = new cn2();
cx.prototype.cx_f = function(){trace("func: cx_f");}
// И сделаем класс-наследник cm2, cn3, cnx и cn2, причем 
// классам cn3 и cnx в качестве стоп-класса укажем cn2.
// Как и раньше, оставим Array в качестве системного
// некопируемого базового класса
classDer3 = multipleInherit(
	function(a, b, c){
		trace("constrOfDerived: " + a + " | " + b + " | " + c);
	},
	[
		[cm2, function(){return [arguments[0]];}, cm1],
		[cn3, function(){return [arguments[1], arguments[2]];}, cn2],
		[cx, function(){return [arguments[1]];}, cn2],
		[cn2, function(){return [arguments[1], arguments[1]];}]
	],
	[Array, function(){super(arguments[1], arguments[2]);}]
);
// Посмотрим, что у нас получилось: под строкой из звездочек
// будет выведен результат работы конструктора
trace("********************************");
od3 = new classDer3("aaaa", "bbbb", "cccc");
trace("-----------------");
// А затем выводим "внутренности" самого объекта
dumpObj(od3, "od3");

Этот код должен выводить результаты работы конструктора и дамп объекта od3. Вот они:

constr cn1: _cn2 | _cn2
constr cn2:  | 
********************************
constr cn1: bbbb_cn2 | bbbb_cn2
constr cn2: bbbb | bbbb
constr cx: bbbb
constr cn3: bbbb | cccc
constr cm2: aaaa
constrOfDerived: aaaa | bbbb | cccc
-----------------
==========================================================
:::::::::::  od3  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
1 : cccc
0 : bbbb
length <hidden>: 2
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od3.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: ,,,
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od3.__proto__.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
length <hidden>: 4
cm2_f : [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: _cn2_cn1,_cn2_cn1
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od3.__proto__.__proto__.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
length <hidden>: 2
0 : _cn2_cn1
1 : _cn2_cn1
cn3_f : [type Function]
cn3_g : [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: _cn2_cn1,_cn2_cn1
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od3.__proto__.__proto__.__proto__.__proto__  ::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
length <hidden>: 2
0 : _cn2_cn1
1 : _cn2_cn1
cx_f : [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: _cn1,_cn1
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::  od3.__proto__.__proto__.__proto__.__proto__.__proto__  ::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
isACopy : THAT'S A COPY
length <hidden>: 2
0 : _cn1
1 : _cn1
cn2_f : [type Function]
cn2_g : [type Function]
__constructor__ <hidden>: [type Function]
constructor <hidden>: [type Function]
__proto__ <hidden>: 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::: od3.__proto__.__proto__.__proto__.__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>: 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od3.__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]
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::  od3.__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]
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
==========================================================

До черты из звездочек идут результаты работы конструкторов при создании прототипа класса cx ; на них мы не будем обращать внимание. А вот дальше начинаются интересные вещи. Во-первых, мы видим, что конструкторы отработали в правильном порядке. То есть - сначала cn1 и cn2, что соответствует тому, что cn2 является виртуальным базовым классом. Затем - cnx, а потом уже cn3, cm2 и конструктор созданного нами производного класса. Все это вполне соответствует заданному нами расположению классов в цепочке.

Наконец, рассмотрев дамп, мы убедимся, что правильный порядок базовых классов соблюдается и там: двигаясь по направлению к корню цепочки, мы обнаружим классы cm2, cn3, cnx, а затем и cn2, cn1 и Array (причем последний - не скопирован, поскольку указан в аргументах multipleInherit как системный базовый класс ; по дампу это, правда, определить нельзя, но можно, например, добавить какой-нибудь метод в системный Array и убедиться, что он сработает и в od3. Разумеется, фокусы наподобие последнего - с модификацией системных классов - лучше делать только в тестовых целях).

< Лекция 7 || Лекция 8: 123456 || Лекция 9 >
алексеи федорович
алексеи федорович
Беларусь, рогачёв
Тамара Ионова
Тамара Ионова
Россия, Нижний Новгород, НГПУ, 2009