Класс Object

Класс Object является надклассом для всех встроенных классов ActionScript. Прототипом его конструктора заканчивается цепочка наследования любого объекта. Это означает, что методы и свойства, присущие объектам класса Object, будут наследоваться объектами всех остальных классов.

Следовательно, они могут быть использованы по отношению к любому объекту, кроме Global и объектов активаций функций, которые не относятся ни к одному классу.

Создание объектов класса Object
Создать объект класса Object можно, используя конструктор и оператор new:

var obj:Object=new Object(); // obj соответствует новый пустой объект

Конструктор Object() может принимать в качестве параметра число, строку или булеву величину.

В результате будет создан объект класса Number, String или Boolean (подобное преобразование автоматически проводится при использовании операторов доступа к свойствам по отношению к величинам элементарного типа). Если параметр равен null или undefined, то возвращен будет пустой объект класса Object. Если параметр указывает на объект составного типа, то он будет возвращен без изменений:

trace(new Object("Привет!!!") instanceof String); // Выводит: true
trace(new Object(_root) instanceof MovieClip); // Выводит: true

Конструктор Object() может быть использован и без оператора new.


В этом случае он является функцией преобразования объектов данных в тип object.

На практике конструктор Object() используется редко. Гораздо чаще объекты данного класса создаются при помощи литералов. Литерал объекта класса Object представляет собой фигурные скобки с заданным в них списком пар свойство—значение:

var obj:Objееt={propl:"Привет",prop2:"Пока");
trace(obj.prop1); // Выводит: Привет

Создание объекта класса Object() c использование литералов позволяет значительно экономить в длине текста программы и делать ее более читабельной. Однако ввиду того, что в парах свойство — значение имя свойства должно быть задано в форме идентификатора, его нельзя, в отличие от применения оператора «[]», прописать в виде числа или использовать и нем служебный символ.

Непосредственно в литерале объекта можно задать и определение метода:

var obj:Object={method:function():Void{trace("Привет")}};
obj.method(); // Выводит: Привет
Литерал объекта может входить в выражения и даже передаваться функции в качестве параметра:
function type(arg):Void {
trace(typeof arg);
}
type({}); // Выболит: object

При написании сценариев объекты класса Object сбычно используются в качестве контейнеров для разного рода служебной информации.


Однако, прежде всего данный класс важен для обеспечения объектной модели ActionScript. И первостепенную роль в выполнении этой функции играют методы класса Object. О них мы и поговорим далее.

Метод registerClass(). Выделение экземпляров клипа в класс
Одна из самых важных и непростых задач из области объектно-ориентированного программирования в стиле ActionScript 1.0 связана с выделением экземпляров одного клипа в отдельный класс.

Соответствующий код получился достаточно непростым, так что вполне вероятно, что у многих читателей возникли трудности с его пониманием. Дня того чтобы упростить выделение экземпляров клипав класс, разработчики ввели в ActionScript мощный метод registerClass():

Object.registerClass(clipID, class);
где:

• Object — так как наследование описываемого метода не имеет смысла, он сохранен в качестве свойства самого конструктора класса Object, а не его прототипа;

• clipID — идентификатор экспорта, заданный для символа клипа в библиотеке. Должен быть
представлен в строковом виде;

• class — класс, в качестве объекта которого должен быть зарегистрирован клип.

Если связывание символа с конструктором класса прошло успешно, то метод registerClass() возвращает true.


В противном случае возвращается false.

Алгоритм работы метода registerClass() следующий. При появлении экземпляра клипа автоматически проверяется, какому символу он соответствует. Если идентификатор экспорта символа совпадает с прописанной в качестве первого параметра метода registerClass() строкой, то экземпляр делается объектом класса, имя которого задано вторым его параметром. Для этого свойству __proto__экземпляра клипа присваивается ссылка на прототип нужного конструктора. Так как класс с экземплярами клипа должен наследовать свойства и методы класса MovieClip, то при активации метода registerClass() класс class делается подклассом MovieClip.

В качестве примера мы решим следующую задачу. Представьте, что на поле имеется множество квадратиков и кружков. Нужно с минимальными затратами труда и оперативной памяти добиться того, чтобы при щелчке по кружку он исчезал. Квадратики же при щелчке по ним пропадать не должны.

Для начала создайте клипы в виде нужных фигур. В качестве параметра для экспорта квадратиков задайте строку «box», а кружков — «ball». Вернувшись на основную временную шкалу, наберите следующий код:

// Конструктор класса, в который будут объединены кружки
function Ball():Void {
// Создаем обработчик, наследуемый всеми объектами класса ball
Ball.prototype.onPress = function():Void { this._alpha = 0; };
// Указываем, что все экземпляры символа с идентификатором экспорта
// "ball" должны заноситься в класс ball
Object.registerClass("ball", Ball);
// Создаем 100 случайно распределенных по полю квадратиков и кружков
for (var i = 0; i<100; i++) {
attachMovie("box", "box"+i, i++, {_x:Math.random()*550,
_y:Math.random()*400));
attachMovie("ball", "ball"+i, i++, {_x:Math.random[]*550,
_y:Math.random()*400});
}

Нажав + , вы убедитесь, что поставленная задача с успехом решена.


Важной особенностью метода registerClass() является то, что он делает объектами формируемого класса не только экземпляры символа, динамически созданные при помощи attachMovie() или duplicateMovieClip, но и те его экземпляры, которые располагаются на временной диаграмме позже кадра, содержащего вызов метода.

Очевидно, что один символ может быть связан с одним лишь классом. При повторном использовании registerClass() произойдет простое переопределение класса, в качестве объектов которого будут регистрироваться экземпляры символа.

Если необходимо приостановить занесение экземпляров символа с идентификатором экспорта SymbolID в класс class, нужно вызвать метод registerClass() со значением null для второго параметра:

Object.registerClass(SymbolID, null);

При этом никаких изменений характера наследования уже созданных экземпляров клипа не произойдет. Однако новые экземпляры связываться с классом class не будут.

При появлении на временной диаграмме (или создании с использованием attachMovie() или registerClass()) нового экземпляра клипа механизм метода registerClass() осуществляет вызов конструктора класса class (без передачи параметров).


Свойству this объекта активации присваивается ссылка на экземпляр клипа. Это означает, что в блоке конструктора может быть задан код, инициализирующий новые или изменяющий существующие свойства экземпляра. Так, добавив в блок конструктора Ball в созданном выше коде следующую строку, вы сделаете все кружки нечувствительными к событию on Press (в результате при щелчке они пропадать не будут):

this.enabled=false;

Используя метод registerClass(), вы должны знать, что может нарушиться наследование методов класса MovieClip. Так, из практики известно, что при этом возникают проблемы с использованием методов removeMovieClip(), getDepth(), swapDepths(), loadMovie(). Причина данной проблемы заключается в том, что метод registerClass() не заменяет значение свойства __proto__ прототипа конструктора класса ссылкой на прототип класса MovieClip. В нем продолжает храниться ссылка на Object.prototype:

trace(Ball.prototype.__proto__==MovieClip.prototype); // Выводит: false
trace(Ball.prototype.__proto__==Object.prototype); // Выводит: true

Исправить описанную недоделку очень просто. Для этого достаточно нужным образом переопределить свойство__proto__прототипа конструктора класса:

Ball.prototype.__proto__=MovieClip.prototype;

Чтобы убедиться, что данный ход работает, замените код в обработчике onPress в приведенном выше примере следующим:

Ball.prototype.onPress = function():Void { this.removeMovieClip();
};

При щелчке по кружку он будет удаляться.

Метод addProperty(). Свойства типа getter/setter
Хороший стиль объектно-ориентированного программирования требует максимально инкапсулировать внутреннюю структуру объекта. В идеале все свойства объекта должны быть скрыты от прямого переопределения, удаления или чтения. Осуществляться же данные операции должны при помощи специальных, доступных пользователю методов. Подобный подход предельно минимизирует вероятность случайного изменения внутренней структуры объекта в процессе выполнения кода.

В универсальных языках программирования, таких как C++, свойства можно разделять на общедоступные (public) и видимые только внутри определения класса (private). Благодаря этому совсем несложно добиться качественной инкапсуляции, сделав методы, работающие со свойствами, видимыми за пределами класса, а сами свойства, скрыв от внешнего доступа. В ActionScript 1.0 не существует аналогов модификаторов public и private языка C++ (но они есть в ActionScript 2.0). Это означает, что полностью скрыть свойства от внешнего доступа вы не можете.

Однако некоторым правилам, усиливающим инкапсуляцию, следовать стоит:

• для удаления, переопределения и чтения свойства объекта класса нужно создавать специальные методы. Их имена могут быть любыми, однако, следуя сложившимся традициям, их лучше начинать с префиксов get (чтение), set (переопределение), delete (удаление);

• при необходимости используйте недокументированную функцию ASSetPropFlags(). Подняв для всех свойств объекта флаги атрибутов DontDelete, Readonly, DontEnum, вы защитите их от переопределения, удаления и перечисления циклом for—in. Это — высшая степень скрытости свойств, доступная в AclionScript 1.0 («официально» она возможна только в случае встроенных объектов языка). Увы, закрыть свойства также и от чтения извне невозможно (но это можно сделать с помощью атрибута private при использовании объектно-
ориентированного программирования в стиле ActionScript 2.0).

Приведем пример класса с повышенной защищенностью свойств:

function Class{par:String):Void { // Создаем конструктор класса
this.property = par; // Соoственное свойство объектов класса
this.getProperty = getPioperty; // Метод, возвращающий значение
// свойства property
this.setProperty = setProperty; // Метод, переопределяющий
// свойство property
ASSetPropFlags(this, null, 3, null); // Защищаем свойства объекта
// от удаления и перечисления
}
// Определение функции для метода getProperty()
function getProperty():String {
return this.property;
}
// Определение функции для метода setProperty()
function setProperty(value:String):Void {
this.property = value;
}
var obj = new Class("Привет!!!"); // Создаем новый объект
trace(obj.getProperty()); // Выводит: Привет
obj.setFroperty("Пока"); // Переопределяем свойство property
trace(obj.getProperty()); // Выводит: Пока (свойство переопределилось)
delete obj.property; // Пробуем удалить свойство property
trace(obj.getProperty()); // Выводит: Пока (свойство удалено не было)
for (var i in obj) { // Пробуем перечислить свойства объекта obj
trace(i); // Окно Output не появляется: свойства защищены от for—in
}

Улучшение инкапсуляции внутренней структуры объекта при помощи методов типа get—set имеет одну отрицательную сторону. В ActionScript пользователи привыкли обращаться к свойствам напрямую, используя, оператор точки. Введение же для этих целей методов делает работу с объектами более сложной, а код — громоздким. Во Flash 6 появился особый метод addProperty() класса Object, позволяющий с легкостью справляться с этим недостатком.

Метод addProperty() создает свойство особого типа getter/setter. При попытке чтения данного свойства вызывается метод, возвращающий значение связанного с ним внутреннего свойства объекта.

Соответственно при переопределении свойства типа getter/setter активизируется метод, меняющий значение скрытого свойства.

Использование метода addProperty() позволяет создавать качественно инкапсулированные свойства с сохранением простого синтаксиса доступа к ним. Поэтому он активно применяется, например, при разработке компонентов.

Метод addProperty() имеет следующий синтаксис:

obj.addProperty("name", getMethod, setMethod);
Здесь:

• obj — объект, для которого должно быть создано свойство типа getter/setter;
• "name" — имя создаваемого свойства;
• getMethod — метод, считывающий значение скрытого свойства, соответствующего getter/setter-свойству. Он будет активизироваться при попытке чтения getter/setter-cвойства. Полученная от него величина возвращается в качестве значения getter/setter-cвойства. Методу getMethod должна соответствовать функция, не принимающая параметров;
• setMethod — метод, устанавливающий значение скрытого свойства. Он вызывался при попытке переопределения getter/setter-свойства. Присваиваемая свойству величина передается в качестве параметра методу setMethod. Соответственно данный метод должен ссылаться на функцию, принимающую один параметр.

Если создание getter/setter-свойства прошло успешно, метод addProperty() возвращает true. В противном случае возвращается false.

В том случае, если свойство должно быть доступно только для чтения, в качестве параметра setMethod нужно прописать null. Отсутствие данного параметра вызовет ошибку. Перепишем созданный выше конструктор Class так, чтобы в нем использовались возможности метода addProperty():

function Class(par:String):Void {
this.property = par;
this.getProperty = getProperty;
this.setProperty = setProperty;
ASSetPropFlags(this, null, 3, null);
// Создаем getter/setter-свойство _property, ссылающееся на методы доступа
// к свойству property
this.addProperty("_property", this.getProperty, this.setProperty);
}
function getProperty():String {
return this.property;
}
function setProperty(value:String):Void {
this.property = value;
}
var obj = new Class("Привет!!!");
trace(obj._property); // Выводит: Привет!!! (считывание значения
// внутреннего свойства происходит верно)
obj._property = "Пока"; // Пробуем переопределить внутреннее свойство
// через getter/setter-свойство
trace(obj._property); // Выводит: Пока (внутреннее свойство
// переопределилось)
trace(obj.property); // Выводит: Пока (считать внутреннее свойство можно
// и напрямую - увы...)
for (var i in obj) { // Просматриваем свойства объекта obj
trace (i); // Выводит: _property (свойство property скрыто
// от перечисления)
}
delete obj._property; // Пробуем удалить getter/setter-свойство
trace(obj._property); // Выводит: undefined (удаление оказалось возможным)
delete (obj.property); // Пробуем удалить скрытое свойство
trace(obj.property); // Выводит: Пока (свойство защищено от удаления)

Среди встроенных свойств ActionScript подавляющее большинство имеет тип getter/setter. Из уже знакомых нам свойств к этому типу относятся _х, _у, _width, _alpha и многие другие. При попытке чтения или переопределения таких свойств вызываются специальные внутренние методы, осуществляющие необходимую операцию.

В среде ActionScript-разработчиков распространено достаточно неоднозначное отношение к методу addPropertyO. С одной стороны, он позволяет улучшать инкапсуляцию объектов, что является достаточно трудноразрешимой проблемой в ActionScript 1.0 (особенно, если не использовать недокументированных возможностей). С другой стороны, введение getter/setter-свойств снижает читабельность кода, а следовательно, повышается сложность его отладки и внесения в него изменений. Разрабатывая классы, вы должны решать, имеется ли объективная необходимость в применении addProperty(), Пожалуй, она существует, если ваш код будет использоваться сторонними разработчиками. Именно поэтому метод addProperty() встречается прежде всего в компонентах.

Методы watch() и unwatch(). События изменений свойств
В предыдущей главе мы определили событие как изменение значения одного или нескольких свойств. Как вы помните, в наиболее общем случае для того, чтобы реализовать собственное событие, достаточно просто циклически проверять значение интересующего свойства. Если окажется, что его величина на данном вызове отличается от зафиксированной при предыдущей активации функции-репортера, факт события считается состоявшимся.

Во Flash 6 появился метод, облегчающий реализацию событий, основанных на изменении свойств.

Это — метод watch() класса Object. Его синтаксис:

obj.watch("name", func, [data]);
Здесь:

• obj — объект, за свойством которого нужно установить наблюдение;
• "name" — имя свойства в строковом представлении;
• func — функция, которая должна быть активизирована при изменении свойства. Например, это может быть метод broadcastMessage(), рассылающий сообщения о событии всем листенерам генератора события;
• [data] — объект данных, передаваемый активизируемой при изменении значения свойства
функции. Необязательный параметр.

Если слежение за свойством было установлено успешно, то метод watch() возвращает true. Иначе возвращается false. Пример:

var obj:Object={prop:1}; // Создаем объект со свойством prop
// При изменении свойства prop в Output будет выведено сообщение
obj.watch("prop", function(){trace("Свойство изменено!")});
obj.prop=2; // В Output появляется: Свойство изменено!

Активируемой в ответ на изменение свойства функции метод watch передает4 аргумента. Первый хранит строку с именем измененного свойства, второй — старое значение свойства, третий — новую его величину, четвертый — объект данных, прописанный в качестве третьего параметра метода watch:

var obj:Object={prop:l};
// Трассируем массив аргументов активизируемой при изменении свойства функции
obj.watch("prop", function(){trace(arguments)},"Свойство было изменено");
obj.prop=2; // Выводит: prop,1,2,Свойство было изменено

Особенностью передаваемого методом watch() активируемой функции списка аргументов является то, что он всегда образован четырьмя элементами. Если необязательный параметр data не был задан, то в качестве четвертого аргумента используется undefined. При попытке передать вызываемой функции вместо одного объекта данных список параметров задействован методом watch() будет только его первый элемент.

Точка слежения для одного свойства может быть установлена только одна. Если вы попытаетесь применить метод watch() но отношению к свойству дважды (например, чтобы при его изменении активизировались две функции), то вторая точка слежения просто заменит первую.

Метод watch() не может быть использован по отношению к свойствам типа getter/setter. Это связано с тем, что значение этих свойств определяется только при их непосредственном вызове, а не при фактическом изменении соответствующих им характеристик. Подобное поведение getter/setter-свойств обусловлено необходимостью минимизировать используемые плейером вычислительные ресурсы машины. Действительно, если бы все встроенные свойства одних лишь клипов (они по типу являются getter/setter-свойствами) обновлялись периодически с достаточной частотой, то для проигрывания среднего фильма не хватило бы мощности самого современного компьютера.

Итак, метод watch() требует, чтобы изменение значения свойства происходило явно. По этой причине он не может следить за getter/setter-свойствами. А так как свойствами этого типа является подавляющее большинство встроенных свойств ActionScript, возможности рассматриваемого метода гораздо более скромны, чем могло бы показаться сначала. Тем не менее, иногда метод watch() может быть чрезвычайно полезен. Например, сего помошью можно реагировать на изменение пользователем текста в поле ввода (т.е. эмулировать событие onChanged):

// Создайте поле ввода и свяжите его с переменной txt
// (для этого имя переменной нужно ввести в строку Var
// Инспектора Свойств поля)
var txt="Привет"; // Переменная поля
// При изменении текста в поле сообщение будет появляться в Output
this-watch("txt", function(){trace("Текст был изменен!");});

Удаление свойства, за которым установлено наблюдение при помоши метода watch(), не будет означать, что уничтожится и точка слежения. При восстановлении удаленного свойства отслеживание его изменений продолжится как ни в чем не бывало. Для того чтобы удалить точку слежения, нужно использовать специальный метод unwatch():

obj.unwatch(prop);
Здесь:

• obj — объект, наблюдение за свойством которого нужно прекратить;
• prop — строка с именем свойства.

Если точка слежения удаляется успешно, метод unwatch() возвращает true. Иначе результат — false.

Прочие методы класса Object
Помимо описанных, класс Object хранит еще 6 методов. Функции, выполняемые гимн методами, достаточно узки. Вот эти «маленькие» методы класса Object:

• valueOf(). В обшем случае данный метод возвращает величину свойства this собственного объекта активации. Обычно это ссылка на объект, вызвавший метод. Но в случае экземпляров классов String, Boolean, Number возвращается связанная с объектом величина элементарного типа:

var obj:Number = new Number(3); // Создаем объект класса Number
trace(typeof obj); // Выводит: object
trace(obj.valueOf()); // Выводит: 3
trace(typeof obj.valueOf()); // Выводит: number (возвращаемая величина
// имеет элементарный тип)

• toString(). Данный метод служит для преобразования объекта в строку. На деле это означает, что он всего лишь возвращает строку [object Object] вне зависимости от того, к какому классу относится объект. Однако в случае объектов таких классом, как Number, String, Boolean, Array, Date LoadVews, XML, преобразование в строку осуществляется по индивидуальному механизму. В частности, для объекта класса Date возвращается строка, содержащая информацию о текущих дате и времени. Алгоритм, лежащий в основе метода toString(), используется также функцией trace:

trace(new Date()); // Выводит к примеру: Thu Oct 16 00:34:12 GMT+0300 2003

• toLocaleString(). Недокументированный метод, выполняющий те же функции, что и toString().

Был введен в язык для соответствия спецификации ЕСМА-262. Никаких заметных отличий между toString() и toLocaleString() не имеется:

var obj:Date=new Date();
trace(obj.toLocaleString()); // Выводит: Thu Oct 16 00:35:49 GMT+0300 2003

• hasOwnProperty («name»), где «name» — строка с именем свойства. Используя данный метод, можно проверить, имеется ли у объекта свойство с определенным именем. В случае наличия свойства метод возвращает true, иначе — false. Свойство считается существующим, если оно является собственным свойством объекта. Наследуемые свойства методом hasOwnProperty() игнорируются. Пример:

// Создаем объект с собственным свойством
var obj:Object={propl:"Привет!!!"};
//Создаем для объекта наследуемое свойство
Object.prototype.рrор2="Пока";
trace(obj.hasOwnProperty("propl")); // Выводит: true
trace(obj.hasOwnProperty("prop2")); // Выводит: false

Метод hasOwnProperty() является недокументированным.

• isPrototypeOf(obj). Данный недокументированный метод позволяет определить, является ли вызвавший его объект прототипом класса, к которому относится объект obj:

function Superclass():Void {} // Создаем конструктор надкласса
function Subclass():Void {} // Создаем конструктор подкласса
// Создаем объект надкласса и делаем его прототипом подкласса
var protObj = new SuperClass();
SubClass.prototype = protObj;
var obj = new SubClass(); // Создаем объект подкласса
trace(protObj.isPrototypeOf(obj); // Выводит: true (protObj
// является прототипом конструктора
// класса SubClass)

• propertyIsEnumerable(«name»), где «name» — строка с именем свойства. Этот недокументированный метод дает возможность узнать, доступно ли свойство для перечисления циклом for—in. Пример:

var obj:Object= (prop:"Привет!!!"};
trace(obj.isPropertyEnumerable("prop")); // Выводит: true
// Защищаем свойство от перечисления
ASSetPropFlags(obj, "prop", 7, null);
trace(obj.isPropertyEnumerable("prop")}; // Выводит: false

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела Action Script на эту тему:
Основные принципы объектно-ориентированного программирования