Flash - статьи

         

Об объектной модели ActionScript


Если вы привыкли работать с объектами в C++, то объекты в ActionScript частично окажутся для вас шокирующими. Для начала: вы привыкли к наличию классов и экземпляров? Придется отвыкать. Классы в ActionScript — это тоже экземпляры, только особого типа. Ну, собственно, если вы задумаетесь, как работают статические методы в классах C++, то поймете, что создавать экземпляр не всегда нужно. Если проводить параллель с ActiveX, то объекты-классы (объекты типа "класс") можно назвать фабриками классов.

Эта каша серьезно усложняет понимание, но не использование классов и объектов. Об объектах-классах также можно думать как о шаблонах, а о производных "экземплярах" как о копиях, создаваемых методом Clone(). Поэтому об объектах-классах еще говорят как об объектах верхнего уровня.

На самом деле вы будете часто пользоваться такими объектами верхнего уровня, предопределенными в самом Flash. Некоторые объекты вообще существуют в одном экземпляре, точнее — не являются экземплярами никаких классов, и создание новых копий не предполагается. На таком объекте выполняются только статические методы, хотя понятие статических методов, как таковых, в ActionScript, не существует.

К более привычным относятся два других типа объектов — "сточные" и пользовательские. Первые заранее определены, и вы обычно создаете их экземпляры. Например, вы создаете новый экземпляр snd=new Sound() для воспроизведения вашего саундтрека. Особый тип объектов, MovieClip, создается специальной функцией. Пользовательские классы создаете вы сами. И тут вас поджидает другой микро-шок.

Этот микро-шок — синтаксис декларирования класса. Его (синтаксиса) нет. Для определения класса используется слово function! Это уже жестоко напоминает "ООП" в perl: оказывается, все реализовано через области видимости, то есть класс определяется областью видимости локальных переменных и функций.

Это стало возможным потому, что, в отличие от C++ и в полном соответствии с Паскалем, допускается создание локальных функций внутри функций, так сказать иерархическая, а не одноранговая архитектура.

На еще более глубоком уровне зарыты локальные анонимные функции — то, что мы назвали бы виртуальными методами или указателями на функции. Эти функции доступны не по имени, а через "хэндлеры", в качестве которых выступают переменные, в частности — элементы списка. Именно так, через список дочерних функций, реализованы области видимости. Виртуальность заключается в том, что вы можете найти нужный метод и переопределить его, независимо от того, писали вы его сами или же унаследовали от суперкласса.

Сами функции, как можно уже догадаться, также являются объектами типа Function. Естественно, что функция содержит списки локальных объектов. Кроме этого функции содержат список своих аргументов (свойство arguments), ссылку на вызывающую и вызываемую функции (то есть на саму себя), а также несколько методов, вроде call и apply. Эти методы явно получают в качестве первого параметра ссылку на объект, из которого вызывается метод, и null, если это "свободная" функция.

Жара, однако, крепчает. Оставим на время эту теорию и проиллюстрируем ее практикой. Не простой, конечно, а относящейся к нашей задачке. Создадим класс, который вычисляет тригонометрические (впрочем, как и любые другие) функции, разделяя окружность на 2n частей. Впоследствии для любого положительного N возвращается значение в этой точке. Также можно спросить, какое "время" соответствует этому N. Параметры на входе конструктора: n, fn(){}, A, w. Где n определяет количество точек, функция задает вычисляемую зависимость, A и w — амплитуда и частота соответственно. Go-go.

function rev(n,fun,a,w) {
this.length=1<N;
  this.mask=this.length-1;
  this.values=new Array(this.length);
  this.slice=Math.PI*2/this.length/w;
  var i=0;
  while (i<THIS.LENGTH) {
    this.values[i]=a*fun(i*this.slice*w); i++; }
}
rev.prototype.GetValue=function(i){ return this.values[i&this.mask];}
rev.prototype.GetSlice=function(){ return this.slice;}
rev.prototype.GetLength=function() {return this.length;}


Вы, возможно, захотите спросить, почему я использую while, а не for. А потому что в for у меня проблемы. Может глюк, а может мне просто ума не хватает. В любом случае, в теории есть только три программных конструкции: последовательность, выбор и итерация — так что разницы никакой. Как видите, код компактный, на вид приятный (и при этом — работающий). Есть пару "хакерских" местечек, например вычисление той же битовой маски или "почему сначала делится на w, а потом умножается". Честно говоря, я и сам ничего не понимаю, просто это вот работает — значит угадал что-то.

Использование этого объекта такое (числа — номера кадров):

1 #include "coord.as"
   #include "rev.as"
   callback=function(arg){return Math.cos(arg);}
   rev1=new rev(10,callback,1,1);
   rev2=new rev(6,callback,2,2);
2 cnt=0;
   while (cnt<REV1.GETLENGTH()*3) {
     dt(cnt*rev1.GetSlice(),rev1.GetValue(cnt),0x00f);
     Cnt++; }
3 cnt=0;
   while (cnt<REV2.GETLENGTH()*3) {
     dt(cnt*rev2.GetSlice(),rev2.GetValue(cnt),0x00f);
     Cnt++; }
4 Stop();


Как видите, мы создаем два объекта, причем отличаются эти объекты тремя параметрами: порядком революции (количеством точек на оборот), амплитудой и частотой. Кроме того, мы проверяем три оборота, чтобы оценить, что наш объект корректно отсекает лишние биты и не ошибается при доступе к массиву. Графически результат выглядит так:



Наши объекты работают ожидаемым образом

Два глюка, кроме for, были обнаружены при работе с as-файлами. Во-первых, вы обязательно должны завершить последнюю строку переводом курсора — иначе ошибки, ошибки... Такие глупости когда-то встречались в некоторых утилитах UNIX — и, как видно, ходят по миру до сих пор. Второе — в режиме Ctrl+Enter какие-то вещи не меняются при изменении внешних файлов. То есть алгоритм обновления кэша включаемых файлов не работает как следовало было. Если ваши изменения в тексте программы не имеют эффекта, попробуйте проверять по Ctrl+F12 — должно сработать. Кстати, и производительность в браузере раза в два больше. Видимо, отключается какая-то отладка или что-то в том же роде.


Содержание раздела