Московский физико-технический институт
Опубликован: 23.12.2005 | Доступ: свободный | Студентов: 2869 / 253 | Оценка: 4.61 / 4.44 | Длительность: 27:18:00
ISBN: 978-5-9556-0051-2
Лекция 2:

Базовые понятия 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));. В этом случае мы могли бы сразу проверить на примитивность не только строку, а вообще какой угодно тип. Скажем, такая проверка сообщит нам, что функции являются объектами. Правда, мы еще ни слова не сказали о том, как во Флэше определять свои функции. К этому мы сейчас и перейдем.