Казахстан, Алматы, Гимназия им. Ахмета Байтурсынова №139, 2008 |
Базовые понятия Action Script
Объектные типы
Объекты во Флэш МХ и похожи, и непохожи на объекты в Java. Общим свойством является то, что все объекты во Флэш доступны только по ссылке. Таким образом, создавая объект любым способом, мы получаем доступ лишь к ссылке на него. Сам объект хранится отдельно и напрямую недоступен. Например, мы пишем
a = new Object();
Теперь в а хранится ссылка на этот объект. Если вы специалист в С++, но не знаете Java, то обратите внимание, что оператор new вернул не указатель (в Java нет указателей), а ссылку. И к полю по имени i объекта a можно обратиться как обычно, через точку:
a.i = 5;
Правда, мы не предпринимали никаких усилий для того, чтобы создать в объекте a поле i. Но во Флэше это необязательно! Как мы увидим далее, поле создастся в таком случае автоматически (что очень плохо для отладки, но хорошо для расширения возможностей Флэш МХ). Кстати, поле i в объекте а теперь является ссылкой на read-only объект, представляющий собой число 5. Кроме ссылок, в объектах Флэш не хранится ничего. Даже методы - и те являются ссылками на объекты-функции (подробнее об этом - далее). Сами же объекты хранятся отдельно и иначе как через ссылки нам недоступны. А объекты, на которые никто не ссылается, удаляет сборщик мусора. Детали работы этого сборщика остаются тайной. И подстегнуть его работу вручную, как это делается в Java, во Флэше, к сожалению, нельзя.
Есть специальное ключевое слово, позволяющее сделать ссылку, ведущую в пустоту. Это слово null. Присваивают null какой-либо ссылке в том случае, когда не хотят ее удалять совсем, но при этом желают явно обозначить, что пока что ссылка никуда не ведет. Но можно и удалить ссылку с помощью ключевого слова delete. Запомните, что во Флэше delete не удаляет объект, на который указывает ссылка. Он просто удаляет саму ссылку (тем самым, освобождая некоторое количество памяти). А объект будет удален сборщиком мусора, если на него больше никто не указывает. Когда настанет время.
Вот взгляните на такой пример:
a = {f: 4, g: 10}; // Создает объект а с полями f = 4 и g = 10 trace(a.g); delete a.g; trace(a.g);
После запуска этот код выводит в консоль
10 undefined
Так что удаление прошло успешно. Обратите внимание на способ создания объекта с заранее заданными значениями ряда полей. В качестве одного из полей можно задать еще один объект, созданный прямо на месте, например вот так:
a = {f: 4, g: 10, k: {e: 50, r: 60}};
Можно даже вызвать функцию у такого созданного "впопыхах" объекта:
a = {f: 4, g: 10, k: {e: 50, r: 60}.toString()}; trace("a.k = " + a.k);
Запускаем (Ctrl+Enter, как обычно) и получаем в консоли
a.k = [object Object]
Значение [object Object] - это то, что функция toString() выводит по умолчанию. Для вашего собственного объекта вы сможете ее переопределить (см. параграф "Функции и методы").
"Наращивание" объекта
Мы сказали, что объект Флэш МХ можно представлять себе как хэш-таблицу. Но хэш-таблица тем и ценна, что можно в нее записывать новые данные. А значит, мы можем создать в объекте новую переменную в любой момент. Есть разные способы создать в объекте новую переменную. Можно так: a.x = 5;. Даже если в объекте а не было переменной х, теперь она будет существовать. (Но если мы просто обратимся к несуществующей переменной, например вот так: trace(a.y), то получим результат undefined. Новая переменная при этом не создается.) Можно также создать новую переменную с именем, заданным в строке. Для этого есть два способа: set("a." + name, 5); и a[name] = 5;. (При создании переменной ей обязательно надо присвоить какое-нибудь значение. Мы в качестве этого значения берем число 5, а можно было бы взять, например, null. Впрочем, создавать поля заранее у объектов Флэш МХ, как вы видите, особого смысла нет. Так что создавайте переменные в тот момент, когда они понадобятся, и присваивайте им именно те значения, которые вам нужны.) Подробнее о способах создания переменных мы еще поговорим. А пока скажем только, что в большинстве случаев второй способ удобнее, хотя он и не является документированным. Как бы то ни было, использование этой экономной записи с квадратными скобками уже практически стало стандартом.
Если же вы в какой-то момент решите, что создали в объекте поля, которые больше там не нужны, - к вашим услугам оператор delete, удаляющий ненужное поле. Причем этому оператору можно передать как ссылку на само поле, так и его строковое имя. То есть в предыдущем примере мы могли написать и вот так: delete "a.g";. Такое поведение характерно для многих встроенных операторов и функций Флэш МХ. Но вот запись
name = "a.g"; delete name;
удалит, разумеется, только поле name того клипа, в кадре которого мы пишем код.
Все лежит в каком-то объекте
Запомните, что во Флэш МХ нет объектов, которые "висят в воздухе", то есть на которые нет ссылок в каком-то другом объекте. Они удаляются сборщиком мусора. Есть ряд системных объектов: это _global, в котором хранятся глобальные данные, и объекты с именами _level0, _level1 и т.д., которые представляют собой ссылки на текущие загруженные флэш-ролики (каждый из которых подгружен из отдельного *.swf-файла). Внутри кода каждого из роликов можно получить ссылку на корневой объект ролика с помощью записи _root. Еще есть системные объекты, соответствующие контекстам вызова функций (о них чуть позже). Если мы будем прослеживать цепочку "владения", то убедимся, что она непременно восходит к одному из вышеописанных системных объектов. То есть, если бы мы имели возможность удалить все эти объекты, то сборщик мусора удалил бы все остальные объекты текущей флэш-программы. Впрочем, объекты, начинающиеся на _level действительно можно выгружать, но это отдельный разговор.
Касается ли сказанное выше примитивных типов? Да, ведь они тоже объекты, только read-only. А локальных переменных? Снова ответ "да". Локальные переменные хранятся в так называемом объекте активации функции ( activation object ), или, как мы обычно будем про него говорить, контексте вызова функции. Далее мы научимся даже получать прямую ссылку на этот объект.
Возникает естественный вопрос: а где лежали переменные из примеров, приведенных выше? Ведь мы создавали их, определяя Action для какого-то кадра линейки времени? Так вот, линейка времени всегда принадлежит какому-либо клипу. Именно в соответствующем ему объекте (типа MovieClip ) и лежат переменные, созданные в Actions. В нашем случае мы вовсе не создавали никакого нового клипа. Так что все переменные попали в корневой клип. Он называется _root. Из любого клипа мы могли бы обратиться к ним, например, так: _root.a.
Все есть ссылка
Видимо, вы уже понимаете, что любая переменная Флэш МХ - это ссылка на какой-то объект. То есть на объект примитивный ( read-only ), либо на объект настоящий. Бывают еще ссылки на функции, но каждая функция - это тоже объект. Хотя функцию можно вызвать, но объект, который ее хранит, свойств хэш-таблицы от этого не теряет.
Проверка на примитивность
Как проверить, примитивными или объектными являются данные, на которые указывает ссылка? Есть два способа. Первый - проверить, сможем ли мы завести у тестируемого объекта новые поля. Если не сможем, то он read-only, то есть примитивный. Второй способ - это использовать оператор instanceof, который обычно применяется для проверки принадлежности к тому или иному классу.
a = new String("Объектная строка"); trace("a = " + a); a.x = 4; // Объектные типы могут наращиваться trace("a.x = " + (a.x == undefined ? "undefined" : a.x)); // А это более простой способ проверки trace("(a instanceof String) = " + (a instanceof String)); trace(""); b = "Примитивная строка"; trace("b = " + b); b.x = 4; // Примитивные - наращиваться не могут, // они read-only. Так что это не сработает. trace("b.x = " + (b.x == undefined ? "undefined" : b.x)); // Используем простой способ проверки trace("(b instanceof String) = " + (b instanceof String));
Выводит такой код вот что:
a = Объектная строка a.x = 4 (a instanceof String) = true b = Примитивная строка b.x = undefined (b instanceof String) = false
Итак, оба способа демонстрируют разницу между объектными и примитивными типами. Несмотря на эту разницу, методы объектной и примитивной строки полностью совпадают. Это неспроста: на самом деле примитивные строки - это действительно объекты типа String, только read-only, и то, что instanceof умудряется заметить разницу - само по себе неожиданно.
Заметим также, что в приведенном выше примере мы могли проверять с помощью instanceof на принадлежность к типу Object: писать trace("(b instanceof Object) = " + (b instanceof Object));. В этом случае мы могли бы сразу проверить на примитивность не только строку, а вообще какой угодно тип. Скажем, такая проверка сообщит нам, что функции являются объектами. Правда, мы еще ни слова не сказали о том, как во Флэше определять свои функции. К этому мы сейчас и перейдем.