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

Наследование во Flash MX

Головоломки для продвинутых

И этот параграф при первом чтении тоже вполне можно пропустить. Нельзя сказать, что он полностью оправдывает свой заголовок. Во-первых, мы долго размышляли над уместностью в названии буквы "р". Во-вторых, содержимое сего параграфа никак не сложнее, скажем, предыдущего; однако, в отличие от него, не несет в себе уж совсем никакой практической пользы. Все, что вы можете от него получить, - это острые программистские ощущения и (ложную) уверенность, что уж теперь-то вы понимаете все детали работы Флэш МХ. Но уж острые ощущения будут что надо, поверьте!

Содержимое контекста вызова

Мы много раз использовали словосочетание "контекст вызова функции" (по-английски его также называют activation object, то есть "объект активации"). Мы даже убедились, что это действительно единый объект, доступный во вложенных функциях через this. Давайте же исследуем его - как исследовали мы другие системные объекты! А заодно - и все остальное, что попадется по дороге. Для этого мы применим вот такой код:

_global.dumpObj = function(obj){
		// Снимаем "защиту" со скрытых полей
	ASSetPropFlags(obj,null,0,1);
	for(name in obj){
		trace(name + ": " + obj[name]);	
	}
}
o1 = {k:10, l:20};
a1 = [5, 6, 7];
trace("******* a1 ********");
dumpObj(a1);
trace("*******************");
trace("");
var f = function(){
	trace("---- arguments -----");
	dumpObj(arguments);
	trace("--------------");
	var x = 5;
	var y = "Текстовая локальная переменная";
	var o2 = {k:15, l:25};	
	var getActivation = function(){
		return this;
	}
	return getActivation();
}
trace("Снаружи this = " + this);
trace("");
trace("==== Контекст вызова f ====");
dumpObj(f(1000, [1, 2, 3]));
trace("===========================");
trace("");
trace(":::::::: _level0 ::::::::");
dumpObj(this);
trace("::::::::::::::::::::::::::");

Посмотрим, как этот код справляется с нашей основной задачей: разобраться, что скрывается внутри контекста вызова функции. Контекст вызова f доступен при вызове getActivation через this. (Здесь важно, что ссылку getActivation мы пометили как var ).

Обратите внимание, что на функции, сгенерированные внутри кадра какого-то клипа (в данном случае, _level0 ), не распространяется это правило. То есть в них this также указывает на _level0, а не на какой-то "контекст вызова кадра". Чтобы это подчеркнуть, мы перед f нарочно поставили var.

Так вот, далее getActivation возвращает ссылку на контекст в саму функцию f, которая, в свою очередь, возвращает его в вызывающее f выражение. То есть мы в очередной раз можем проверить, что контекст вызова не перестает существовать после того, как функция отработала, если на него остались какие-то ссылки. Посмотрим, что получается на выходе:

******* a1 ********
2: 7
1: 6
0: 5
__proto__: 
constructor: [type Function]
length: 3
*******************
Снаружи this = _level0
==== Контекст вызова f ====
---- arguments -----
1: 1,2,3
0: 1000
caller: null
callee: [type Function]
__proto__: 
constructor: [type Function]
length: 2
--------------
getActivation: [type Function]
o2: [object Object]
y: Текстовая локальная переменная
x: 5
arguments: 1000,1,2,3
this: _level0
===========================
:::::::: _level0 ::::::::
f: [type Function]
name: name
__proto__: [object Object]
constructor: [type Function]
a1: 5,6,7
o1: [object Object]
$appPath: file:///K|/Program%20Files/Macromedia/Flash%20MX/
$version: WIN 6,0,21,0
::::::::::::::::::::::::::

Мы видим, что в контексте вызова функции хранятся все локальные переменные, а также ссылки на arguments и this. Также видно, что объект arguments отличается от обычного массива (для примера и проверки работы функции dumpObj выведены "внутренности" массива a1 ) только полями caller и callee. Интересен также тот факт, что у объекта "контекст вызова" отсутствуют поля constructor и __proto__. Это наводит на мысль о некоторых трюках, которые мы испробуем в следующем подпараграфе. Напоследок давайте посмотрим, какие из увиденных нами полей являются открытыми, а какие мы видим только благодаря применению недокументированной функции ASSetPropFlags. Закомментируем ее вызов в функции dumpObj, и тогда на выходе получим следующее:

******* a1 ********
2: 7
1: 6
0: 5
*******************
Снаружи this = _level0
==== Контекст вызова f ====
---- arguments -----
1: 1,2,3
0: 1000
--------------
getActivation: [type Function]
o2: [object Object]
y: Текстовая локальная переменная
x: 5
===========================
:::::::: _level0 ::::::::
f: [type Function]
name: name
a1: 5,6,7
o1: [object Object]
$version: WIN 6,0,21,0
::::::::::::::::::::::::::

Примечательно, что в объекте arguments мы не видим теперь ничего, кроме самих аргументов, а в контексте вызова видим только локальные переменные (но не видим arguments и this ).