Казахстан, Алматы, Гимназия им. Ахмета Байтурсынова №139, 2008 |
Наследование во Flash MX
Вывод полного содержимого объекта
В этом небольшом разделе мы поговорим о том, какие есть удобные способы вывести полную таблицу полей и методов объекта - таблицу, в которой все переменные и функции рассортированы по принадлежности к одному из прототипов в цепочке __proto__.
Для системных объектов вывести все их содержимое в правильном порядке довольно просто (поскольку содержимое прототипа всегда скрыто). Алгоритм здесь такой: при помощи for...in выводим поля, непосредственно принадлежащие объекту. Возможно, их надо будет сначала раскрыть при помощи ASSetPropFlags. Затем в качестве текущего объекта выбираем прототип, то есть содержимое ссылки __proto__. И повторяем вышеописанную операцию. Так действуем до тех пор, пока в __proto__ хоть что-нибудь есть (на деле мы всегда упираемся в Object ). C произвольным объектом такая операция не пройдет, поскольку в for...in сразу же обнаружатся поля из прототипов различных уровней вложенности. Можно, конечно, разыскать все эти прототипы заранее и скрыть все присутствующие в них поля и методы. Но если уж применять подобные "силовые приемы", то лучше воспользоваться временным "отцеплением цепочки", о котором мы сейчас расскажем.
Идея этого метода состоит в следующем: мы временно записываем в поле __proto__ значение null (сохранив куда-нибудь старое содержимое поля). В результате в нашем объекте останутся доступными только его собственные поля - мы как бы отключаем все базовые классы. Выводим эти поля, возвращаем ссылку __proto__ к прежнему состоянию, затем в качестве текущего объекта берем тот, на который ссылается __proto__, и повторяем все эти операции уже для него. В результате мы не только получим полный список полей, рассортированный по прототипам, в которых эти поля лежат. Мы еще и сможем увидеть в этом списке те поля, которые были переопределены где-то в цепочке __proto__ ближе к исследуемому объекту (тому, с которого мы начали всю процедуру). Таким образом, эта методика дает самую полную информацию о содержимом всех надклассов (они же суперклассы, они же базовые классы разного уровня) исследуемого объекта. Вот код, который сию замечательную методику реализует.
// Печатаем имя и значение поля, но не смешиваем // пустую строку и undefined _global.printField = function(name, value){ if (value == undefined) value = "undefined"; trace(name + ": " + value) } // Печатаем все, что доступно функции for...in // Чтобы увидеть скрытые поля, снимаем с них защиту // Если не предпринять никаких мер, эта функция // выведет как непосредственные поля и методы объекта, // так и открытое содержимое его прототипов всех уровней. _global.printFieldsByForIn = function(obj, str, tempProto){ trace("::::::::::: " + str + " ::::::::::::"); trace(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); // Снимаем "защиту" со скрытых полей. ASSetPropFlags(obj, null, 0, 1); for(var name in obj){ // Принимаем меры для того, чтобы наши действия // с обнулением __proto__ не отражались на выводимой // информации. if (name == "__proto__") printField(name, tempProto); else printField(name, obj[name]); } trace(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); } // В этой рекурсивной функции мы, собственно, и реализуем // вышеописанный фокус с "отцеплением цепочки" __proto__ . _global.printAllFields = function(obj, name){ var tempProto = obj.__proto__; obj.__proto__ = null; printFieldsByForIn(obj, name, tempProto); obj.__proto__ = tempProto; // Проверка на null не нужна: null == undefined, хотя // отличить их и можно при помощи оператора ===. if (obj.__proto__ != undefined) printAllFields(obj.__proto__, name + ".__proto__"); } // А эта функция просто вызывает основную рабочую функцию и // добавляет "элементы оформления" (в текстовом виде, разумеется). _global.dumpObj = function(obj, name){ trace("================================================"); if (name == undefined) name = "<Dumped object>"; printAllFields(obj, name); trace("================================================"); trace(""); } // Теперь тестируем функцию dumpObj на разных объектах. // Сначала на простейшем объекте с одним полем. myObject = new Object(); myObject.a = 10; dumpObj(myObject, "myObject"); // Затем тестируем на массиве. someArray = [1, 2, 4, 7]; dumpObj(someArray, "someArray"); // Далее тестируем на примитивной сроке. a = "Примитивная строка"; dumpObj(a, "Примитивная строка"); // И, наконец, на системном объекте _root, который // представляет из себя корневой MovieClip. dumpObj(_root, "_root");
Выводит этот код очень большое количество текста, но текст этот того стоит - ведь это внутренности разных системных объектов, что может быть интереснее? Вот что выводится в консоль:
========================================================== ::::::::::: myObject :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: a: 10 __constructor__: [type Function] constructor: [type Function] __proto__: [object Object] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: myObject.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: __proto__: undefined toLocaleString: [type Function] isPropertyEnumerable: [type Function] isPrototypeOf: [type Function] hasOwnProperty: [type Function] toString: [type Function] valueOf: [type Function] addProperty: [type Function] unwatch: [type Function] watch: [type Function] constructor: [type Function] ========================================================== ::::::::::: someArray :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 3: 7 2: 4 1: 2 0: 1 __proto__: constructor: [type Function] length: 4 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: someArray.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 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] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: someArray.__proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: __proto__: undefined toLocaleString: [type Function] isPropertyEnumerable: [type Function] isPrototypeOf: [type Function] hasOwnProperty: [type Function] toString: [type Function] valueOf: [type Function] addProperty: [type Function] unwatch: [type Function] watch: [type Function] constructor: [type Function] ========================================================== ::::::::::: Примитивная строка :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: Примитивная строка.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: substr: [type Function] split: [type Function] substring: [type Function] slice: [type Function] lastIndexOf: [type Function] indexOf: [type Function] concat: [type Function] charCodeAt: [type Function] charAt: [type Function] toLowerCase: [type Function] toUpperCase: [type Function] toString: [type Function] valueOf: [type Function] __proto__: [object Object] constructor: [type Function] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: Примитивная строка.__proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: __proto__: undefined toLocaleString: [type Function] isPropertyEnumerable: [type Function] isPrototypeOf: [type Function] hasOwnProperty: [type Function] toString: [type Function] valueOf: [type Function] addProperty: [type Function] unwatch: [type Function] watch: [type Function] constructor: [type Function] ========================================================== ::::::::::: _root :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: toLocaleString: [type Function] isPropertyEnumerable: [type Function] isPrototypeOf: [type Function] hasOwnProperty: [type Function] toString: [type Function] valueOf: [type Function] addProperty: [type Function] unwatch: [type Function] watch: [type Function] a: Примитивная строка someArray: 1,2,4,7 myObject: [object Object] __proto__: [object Object] constructor: [type Function] $appPath: file:///K|/Program%20Files/Macromedia/Flash%20MX/ $version: WIN 6,0,21,0 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: _root.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: createTextField: [type Function] clear: [type Function] endFill: [type Function] lineStyle: [type Function] curveTo: [type Function] lineTo: [type Function] moveTo: [type Function] beginGradientFill: [type Function] beginFill: [type Function] createEmptyMovieClip: [type Function] stopDrag: [type Function] startDrag: [type Function] removeMovieClip: [type Function] duplicateMovieClip: [type Function] gotoAndStop: [type Function] gotoAndPlay: [type Function] prevFrame: [type Function] nextFrame: [type Function] stop: [type Function] play: [type Function] setMask: [type Function] getDepth: [type Function] attachVideo: [type Function] attachAudio: [type Function] getBytesLoaded: [type Function] getBytesTotal: [type Function] getBounds: [type Function] hitTest: [type Function] globalToLocal: [type Function] localToGlobal: [type Function] swapDepths: [type Function] attachMovie: [type Function] loadMovie: [type Function] loadVariables: [type Function] unloadMovie: [type Function] getURL: [type Function] meth: [type Function] tabIndex: undefined enabled: true useHandCursor: true __proto__: [object Object] constructor: [type Function] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: _root.__proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: __proto__: undefined toLocaleString: [type Function] isPropertyEnumerable: [type Function] isPrototypeOf: [type Function] hasOwnProperty: [type Function] toString: [type Function] valueOf: [type Function] addProperty: [type Function] unwatch: [type Function] watch: [type Function] constructor: [type Function] :::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Предоставив вам самостоятельно разглядывать разнообразные недокументированные функции, названия которых у нас сейчас распечатались (наряду со всем остальным), мы дадим все же несколько комментариев. Во-первых, разумеется, необязательно было снимать защиту со всех полей и методов. Если вы будете использовать подобную функцию для распечатки содержимого собственных классов, мы рекомендуем вам закомментировать соответствующую строчку.
Во-вторых, вы можете заметить, что наш фокус с обнулением __proto__ не сработал на клипе _root - в него все равно попали методы из класса Object. (Почему только из Object? Дело в том, что непосредственный прототип объекта _root - это MovieClip, но снять защиту с его методов мы к моменту вызова dumpObj для _root еще не успели. А с Object защита уже была снята.) В остальных случаях такого не произошло. Отсюда вывод: все дело в том, что _root - слишком важный системный объект, чтобы позволять его поведению зависеть от того, что лежит у него в __proto__. (А, может, дело в том, как именно было удобнее всего реализовать корневой клип.) Во всяком случае, о том, что за объект является прототипом клипа _root, система Flash MX догадывается без обращения к полю _root.__proto__ и поэтому изменение его значения ничего не дает. О том, что это п оведение не является общим для других клипов, можно узнать, вызвав функцию dumpObj для произвольного клипа. Для этого можно в конец вышеприведенного кода вставить такие строчки:
_root.createEmptyMovieClip("newClip", 1); dumpObj(newClip, "mc");
Только что раскрытые функции Object и MovieClip в секции, где выводятся собственные поля и методы клипа newClip, не обнаруживаются.
Доступ к базовому классу: ключевое слово super
Вы уже знаете два способа обратиться из производного класса к базовому. Первый - с помощью apply или call (при этом используется явное имя класса и ссылка prototype ). Можно также добраться до нужных функций прямо из объекта через this.__proto__.__proto__ (а потом опять использовать apply ). Этот способ хуже, поскольку при вызове функции, содержащей такой код, из классов, производных от нашего, значение ссылки this.__proto__.__proto__ будет другим.
Но есть в системе Флэш МХ и механизм, который специально предназначен для решения рассматриваемой задачи. Этот механизм программистам, применяющим в своей работе язык Java, прекрасно известен. Основан он на использовании ключевого слова super . С помощью этого ключевого слова можно вызвать конструктор базового класса - пишем super (arg1, arg2, ...). Список аргументов ничем не отличается от списка аргументов любой другой функции. При этом конструктор вызывается не как обычная функция (в которой this означает объект, в котором лежит ссылка на нее), а именно как конструктор ( this указывает на вновь созданный объект). Если же нужно обратиться к какому-либо полю или методу базового класса, используется конструкция вида super.a или super .func() (что, как правило, применяется при вызове базового варианта переопределяемой виртуальной функции).
Обратите внимание, что super - это не просто ссылка, также как и this, поскольку имеет специальное значение в полиморфных классах. Ведь super , использованный в методе некоторого класса, всегда будет означать ссылку на его непосредственного предка, даже когда вызов произошел из далекого производного класса (в методах которого super имеет совсем другое значение). Вы можете представлять себе это так: компилятор Флэш, видя в коде ключевое слово super , вставляет на его место нечто вроде ссылки непосредственно на базовый класс (его прототип). В случае вызова конструктора эта псевдоссылка будет указывать на сам конструктор базового класса.
Последняя особенность "ссылки" super вытекает из предыдущих: перед ней не надо писать this.