Россия |
Наследование во 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 ).