Казахстан, Алматы, Гимназия им. Ахмета Байтурсынова №139, 2008 |
Наследование во Flash MX
Эмуляция with в функциях
Сейчас мы видели, что контекст вызова функции - это объект, не являющийся экземпляром какого-либо класса. Об этом говорит отсутствие ссылки __proto__. То есть у объекта нет прототипа и поля у него заведены только те, что нужны в данном конкретном случае. (Можно, конечно вспомнить про повторяющиеся, хоть и скрытые, поля arguments и this. Но факт остается фактом - эти поля тоже берутся не из прототипа, а заводятся каждый раз заново.) Однако мы уже знаем, что при наследовании в стиле rebel можно "прицепить" базовые классы потом, уже после создания уникальных полей. Для этого всего лишь надо установить ссылку __proto__. Возникает соблазн проделать то же самое и с контекстом вызова. В результате поля того объекта, на который будет указывать __proto__, будут восприниматься так же, как собственные поля контекста вызова. То есть ... как локальные переменные! Это настолько неожиданный вывод, что хочется немедленно его проверить.
_global.dumpObj = function(obj){ // Снимаем "защиту" со скрытых полей ASSetPropFlags(obj,null,0,1); for(name in obj){ trace(name + ": " + obj[name]); } } o1 = {k:10, l:20}; var f = function(){ var x = 5; var y = "Текстовая локальная переменная"; var o2 = {k:15, l:25}; var getActivation = function(){ return this; } var act = getActivation(); act.__proto__ = o1; // Или var.__proto__ = o1; trace("==================="); trace("k = " + k); trace("l = " + l); trace("==================="); k = 145; var l = 1111; trace("==================="); trace("k = " + k); trace("l = " + l); trace("==================="); return getActivation(); } trace("==== Контекст вызова f ===="); dumpObj(f(1000, [1, 2, 3])); trace("==========================="); trace(""); trace("******* o1 ********"); dumpObj(o1); trace("*******************"); trace(""); trace(":::::::: _level0 ::::::::"); dumpObj(this); trace("::::::::::::::::::::::::::"); На выходе получаем: ==== Контекст вызова f ==== =================== k = 10 l = 20 =================== =================== k = 10 l = 1111 =================== k: 10 l: 1111 __proto__: [object Object] act: [object Object] getActivation: [type Function] o2: [object Object] y: Текстовая локальная переменная x: 5 arguments: 1000,1,2,3 this: _level0 =========================== ******* o1 ******** k: 10 l: 20 __proto__: [object Object] constructor: [type Function] ******************* :::::::: _level0 :::::::: name: name __proto__: [object Object] constructor: [type Function] k: 145 f: [type Function] o1: [object Object] $appPath: file:///K|/Program%20Files/Macromedia/Flash%20MX/ $version: WIN 6,0,21,0 ::::::::::::::::::::::::::
Мы видим удивительные (хотя и ожидавшиеся нами) вещи! Действительно, переменные k и l воспринимаются внутри функции (после того, как контексту указали __proto__ ) как локальные! Но более того, они еще и являются чем-то вроде константных локальных переменных! Наша попытка изменить переменную k привела к тому, что была заведена соответствующая переменная в _level0. (Поскольку содержимое объекта, на который указывает __proto__, не меняется, то должна быть заведена новая переменная; но поскольку ключевое слово var не было применено, то переменная заводится в объекте, из которого была вызвана функция f.) Поскольку приоритет в функции имеет локальная переменная, то значение k как бы и не изменилось. В случае же переменной l, когда мы использовали var, значение l в функции, конечно, было заменено. Но в объекте o1 оно, разумеется, осталось п режним (так же как и значение k ). Конечно, польза такого необычного применения __proto__ далеко не очевидна. Однако сама возможность подобных фокусов вызывает теплые ностальгические чувства и воспоминания о Бэйсике и ассемблере конца 80-х годов прошлого века. Не будем вдаваться в подробности. У каждого своя ностальгия.