Здравствуйте, гость ( Вход | Регистрация )

Свернуть

Новости

Форум Лучшее из галереи Уроки и статьи
07.12.2015 Выставочный зал: кошарик - персональная выставка
31.08.2015 Интересные ссылки для рисовальщиков
21.01.2015 Выставочный зал 2: Игрушки Олеси Гавриленко

27.12.2014 Выставочный зал: кошарик - персональная выставка
17.11.2014 Дуэль "Рыбки" - победитель Лисичка
05.11.2014 Конкурс иллюстраций "Снежная королева", до 31 января
30.10.2014 Дуэль "Рыбки" до 16 ноября
14.07.2014 Мастер-класс Мини-мишка в технике фелтинга
26.05.2013 Ау! Мы ищем таланты! – приглашаем модераторов!
«Без тебя»
tn_gallery_974_112_48270.jpg

©jokerom
06.01.2016 Виртуальный Музей: Русский живописец Василий Дмитриевич Поленов
28.12.2015 Виртуальный Музей: Нидерландский живописец Квентин Массейс
16.12.2015 Виртуальный Музей: Итальянский живописец Франче́ско Айец
17.11.2015 Виртуальный Музей: Луи Анкетен (Louis Anquetin)
11.11.2015 Виртуальный Музей: Русский живописец Алексей Иванович Корзухин
Файловый архив
06.09.2013 Прочее: Файлы к уроку "Чайная церемония"
05.09.2013 Журнал Art Tower: ArtTower Magazine #8
16.05.2013 Adobe Photoshop: Кисти: Reid Southen brush
16.05.2013 Adobe Photoshop: Кисти: Goro Fujita brush
16.05.2013 Adobe Photoshop: Кисти: Кисти для рисования в Photoshop
Блоги Новости в цифровом мире и мире дизайна
02.12.2014 Дама с каменьями: Вести с крыши 2
08.11.2014 Timenews: Вассерман: прежняя модель мировой экономики исчерпала себя
06.11.2014 Дама с каменьями: Приятные вести с крыши))) от Гаргула)
02.11.2014 Spell: Книги Дж. Кэмерон
22.10.2014 Vjaz: от ФУ до МА
25.11.2015 Комментарий от Foxx в Costa Rica Adventure Divers, Логотип для компании и рисунок на майку (maria_mer)
18.11.2015 Комментарий от maria_mer в Spellforce - майка, для фанов игры (maria_mer)
18.11.2015 Комментарий от maria_mer в Белая книга. Целитель - любительский прект (maria_mer)
09.04.2015 Комментарий от Romana в Книги Дж. Кэмерон (Spell)
08.04.2015 Комментарий от Romana в Я решил вернуться... (Элбирет)
16.03.2015 ФОТОФОРУМ-2015
01.01.2015 ARQUTE.com и ArtTalk.ru закрываются
19.01.2017 Конкурс дизайна логотипов
26.12.2016 ру/Ководство: О творческом развитии
14.10.2016 ру/Ководство: Разнообразие

 
Добавить ответ в эту темуОткрыть тему
> Основоположения программирования: часть VI.1 - функции и области видимости, Функции: подробности. Области видимости переменных. Глобальные функции.
V
Des
сообщение 25.08.2009 - 09:35
Сообщение #1


тритониус
****

Звезда писателя I степениЗа вклад в развитие ArtTower.ru
Группа: Почетные граждане
Сообщений: 728
Регистрация: 9.12.2007
Из: Москва \ Питер
Пользователь №: 6553
дышу под водой
Галерея Блог


Симпатии:  68  


Предыдущая часть: Основоположения программирования - часть V.4
Начало: Основоположения программирования - часть I

1. Функции - подробности

О том, что такое "функция" (function) я рассказывал в части II.2 Введение: процедурные и ООП языки. Теперь же настало Время познакомиться с функциями и их применением поглубже - чтобы, вооружась этими знаниями, двигаться дальше - к работе с объектами, программному рисованию и другим интересным вещам.
Итак, функция - это некоторый участок кода, который выполняет то или иное действие, возможно, получая для выполнения этих действий какие-то аргументы и, возможно, возвращая какое-либо значение. Чтобы непосредственно выполнить действие, описанное функцией, её нужно вызвать, возможно, передав ей для работы нужные значения аргументов, и для того, чтобы ей пользоваться, ее нужно определить. Результат работы функции - это или непосредственно то действие, которое она выполняет (напр., функция getURL(url:String) откроет страницу по адресу, записанному в строке url), либо значение, которое функция возвращает, и которое можно сохранить в переменной либо сразу, никуда не сохраняя, использовать.
В пределах одного фрейма ("кадра") функция может быть задана где угодно. Но вот если код "разбросан" по нескольким фреймам - то функцию можно вызвать только в том же или последующим фрейме по отношению к тому, в котором она задана. Т.е. если мы определим функцию в фрейме 3, то вызвать её из фрейма 1 не получится - произойдёт ошибка.
Функция, определённая внутри класса, называется методом, и для таких "функций" есть особенные правила использования. Однако об этом мы поговорим позже - когда будем учиться создавать собственные классы и объекты.
А пока продолжим изучать "обычные" функции, которые чаще всего в AS2 и используются1.
И для начала - подробнее о видах функций и способах их задания. Да, функции в AS2 бывают разных видов, и способ задания функции определяется тем, как мы собираемся функцию использовать. Тут потребуется толика внимательности, но усилия будут вознаграждены, где-то именно здесь наступает первое просветление, сансара незнания отпускает вас, а нирвана свободного флэш-полёта становится ближе smile.gif
Итак: самый распространённый вид функций - это функции именованные. Они задаются оператором function в следующем виде:
function functionName(arg1:Type,arg2:Type,arg3:Type = value):Type { <...> }
здесь:
- function - оператор, говорящий компилятору о том, чем мы, собственно, в этой строке занимаемся
- functionName - имя функции (функция-то наша - именованная, вот оно и имя...) Придумываем сами, стараясь, чтобы не совпало ни с какой другой нашей функцией и ни с какой глобальной.
- круглые скобки, внутри которых - аргументы, передаваемые функции. Если никаких аргументов передавать не нужно, то внутри скобок будет пусто, но сами скобки всё равно нужны
- аргументы, которые перечисляются через запятую. После двоеточия для каждого аргумента указывается Тип Данных, к которому аргумент принадлежит.
Вообще, Тип Данных надо указывать всегда, когда только возможно (т.е. когда он заранее известен - а так оно почти всегда и есть). Это реально и ощутимо экономит память и увеличивает производительность, и, кроме того, такой код гораздо читаемее - т.е. вам самим же в первую очередь через месяц-другой будет гораздо проще в нём разобраться.
Для аргументов можно указать значения по умолчанию, как это сделано для arg3 в приведённом примере. Значения по умолчанию будут использованы тогда, когда при вызове функции аргумент, стоящий на соответствующем месте, не задан. В противном случае функции будет передано то значение аргумента, которое задано при вызове.
После круглых скобок стоит двоеточие и Тип Данных - это Тип Данных возвращаемого функцией значения. Если значение не возвращается, нужно указать Тип Данных Void. Напр.:
CODE
/*
Эта функция добавит перед именем файла страницы путь
к подкаталогу "html" и откроет эту страницу в новом
окне (закладке) броузера - если, конечно, в этом подкаталоге
есть соответствующий файл.

*/
function getHtmlURL(page:String = 'about.html'):Void {
var url:String = "./html/" + page;
getURL(url,'_blank');
}

// вызов функции
getHtmlURL('myPage.html');
// этот вызов функции вызовет открытие файла './html/myPage.html' в новом окне (закладке) броузера.
getHtmlURL();
// а этот - попытку открытия файла './html/about.html'


Возвращаемое значение - всегда единственное. Если нужно получить от функции несколько возвращаемых значений, используют массив, объект или структуру данных, заданную в пользовательском классе (это мы изучим позднее).
В определении функции возврат значения (и одновременно - прекращение выполнения функции) указывают оператором return.
Например:

CODE
function squareDown(coordX:Number):Number {
var yy:Number = 100 - (0.3 * coordX * coordX / 2);
return yy;
}
var coordY0:Number = squareDown(0);
var coordsY:Array = new Array();
for (var i:Number = 1; i < 20; i++) {
coordsY.push(squareDown(i));
}

/*
в результате выполнения этого кода в переменной coordY0
будет находиться значение функции squareDown(0), а в массиве
arr - значения этой функции для аргументов от 1 до 19 с шагом 1,
причём в значение функции для аргумента 1 будет находиться в
элементе arr[0]
*/


А с передачей значений по ссылке на массив (вы ведь помните, что для комплексных Типов Данных параметры и аргументы передаются по ссылке? - если нет, то посмотрите урок о массивах, Основоположения программирования. Часть Iv.3) подобная задача решается так:
CODE
// функция, которая вычисляет одно значение по формуле:
function coordY(xx:Number):Number {
return 100 - (0.3 * xx * xx / 2); // кстати, скобки здесь только для удобочитаемости...
}
// функция, которая возвращает ссылку на массив значений:
function coordsY(coordX0:Number,stepsNum:Number,step:Number):Array {
var retArr:Array = new Array();
var i:Number = 0;
while(i<stepsNum) {
if (i == 0) {
retArr.push(coordY(coordX0));
} else {
retArr.push(coordY(retArr[i-1]+step));
}
i++;
}
return retArr;
}
// вызов функции, вычисляющей массив значений:
var yValues:Array = new Array();
yValues = coordsY(1,20,0.45);
// теперь в массиве yValues находятся вычисленные значения
// для 20 элементов, начиная с элемента, вычисленного
// от аргумента 1, с шагом 0.45.


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

Именованные функции используются в большинстве случаев. Однако иногда требуется и другой вид функции в флэш - неименованная ("анонимная") функция.
Само по себе название уже говорит о том, что у таких функций нет собственного имени, т.е. того, что стоит в задании именованной функции после оператора function. Но тогда как же с ней обращаться, т.е. - вызывать её? А вот как:
Во-первых, все функции в Flash (в т.ч. и именованные, хотя это практически не используется) - это объекты определённого Типа. Для них существует специальный Тип Данных - Function. А раз это объект, то можно... создать переменную соответствующего Типа и присвоить ей функцию... как значение! - или, если быть более точным, как ссылку на значение. Вы еще не потеряли нить повествования? - если даже и потеряли, то пример поможет вам разобраться:
CODE
// задаём переменную Типа Function, задаём неименованную функцию
// (для простоты она просто возвращает true)...
var myF:Function = function():Boolean { return true; }
//...и вызываем её где нам нужно:
if (myF()) { trace ("function OK"); }


Между именованными и неименованными функциями есть одно весьма существенное различие: неименованную функцию нельзя использовать (вызвать) до того, как она задана. Т.е. если поменять в последнем примере строки местами - ничего не получится, кроме ошибки, в то Время как с именованными функциями всё будет в порядке.
По способу задания именованные функции задаются оператором function, в то Время как неименованные - выражением (литералом). Т.о. именованные функции "просто определяются", в то Время как неименованные - "вычисляются", т.е. сама функция является "вычисляемым объектом". Это весьма глубокое концептуальное различие, но, однако - ведь вас интересует практика?
Казалось бы - зачем нужны неименованные функции, когда есть именованные? - а вот зачем: во-первых, они используются в качестве хэндлеров - обработчиков событий. Когда мы в уроке о событиях (Основоположения программирования: часть V.1писали:
CODE
btn_mc.onRollOver = function() { this.gotoAndStop(2); }
btn_mc.onRollOut = function() { this.gotoAndStop(1); }

- то тем самым мы задавали две неименованные функции и присваивали ("присоединяли", как часто, хотя и неверно, говорят в русскоязычных руководствах) их свойствам (т.е. - переменным класса) onRollOver и onRollOut объекта btn_mc. Для этого применения имя функции просто не нужно. Мы даже не задумываемся о том, что все эти "onRollOver", "onRollOut", "onLoad" и т.п. свойства - это фактически переменные определённого Типа (Function), которым мы, назначая обработчик, присваиваем значения, и значением этим является объект Типа Function - однако так это и есть.
Для чего нам это понимать? - ну, например, чтобы свободнее себя чувствовать в обращении с такими объектами. Ведь уже гораздо проще понять, почему для "очистки" хэндлера (перехватчика событий) можно написать просто:
CODE
btn_mc.onRollOver = function(){}

- всё верно, мы ведь просто-напросто присваиваем свойству другое значение. А можно создать целый массив объектов Function и выбирать из него по мере надобности - для того же обработчика событий... Применений масса.

Во-вторых, неименованные функции применяются везде, где по имени обращаться не нужно - в частности, в вызовах функций "по таймеру" вроде
CODE
var intID:Number;
// эта функция будет печатать в окошке Output
// соответствующую строку с интервалом в пол-секунды...
// если дальше в коде нет вызова clearInterval(intID) - бесконечно
intID = setInterval(function():Void{ trace('interval function called')},500


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

2. Области видимости: _root, local и _lockroot

До сих пор мы писали "var myVar:Type = ...", "function myFunc():Type {}" и т.п. выражения, не задумываясь особенно о том, доступен ли этот участок кода из других частей нашей программы, или нет. Однако - пора задуматься, поскольку не всё так просто: "видимость" (доступность) той или иной переменной (и неименованной функции, кстати, и метода - но о методах мы будем говорить, когда, наконец, доберёмся до объектов) из другого места программы зависит от того, находится ли эта переменная в той же области видимости.

Честно говоря, мне пришлось изрядно подумать, как рассказать о довольно запутанной системе "областей видимости" в Flash \ AS (да-да, здесь имеет значение не только сам язык, но и "среда исполнения" - ролик и таймлайн). И решил, что самым понятным будет - привести примеры неработающего кода, а потом разобраться, почему он не работает и как на самом деле надо... Итак, примеры кода, который не заработает:
CODE
// -------------
// Пример 1.
// Весь код в первом фрейме
// единственной сцены основного таймлайна

function addFive():Void {
var numFive:Number = 5;
// <...> нам неважно, что ещё делает эта функция
}
var num:Number = numFive;
// ожидается, что в переменой num будет находиться значение 5.
// однако - не будет, более того, произойдёт ошибка
// -------------
// Пример 2.
// Этот код - в первом фрейме основного таймлайна:
var n1:Number = 2;
var n3:Number = n1+n2;
// ...а этот - в первом фрейме ролика, находящегося
// на сцене и не имеющего имени экземпляра.
var n2:Number = 3;
// Ожидается, что в переменной n2 будет значение 5.
// однако - не будет, более того, произойдёт ошибка
// -------------
// Пример 3.
// Этот код - в swf-ролике, подгружаемом в основной ролик:
var num1:Number = 2;
var num2:Number = 3;
var num3:Number;
// а этот - во вложенном в этот подгружаемый ролик ролике (MovieClip):
_root.num3 = _root.num1 + _root.num2;
// ожидается, что в переменной num3 будет значение 5
// однако, как вы догадались - ничего подобного там не будет.


Итак: почему не заработает Пример №1?
Дело в том, что переменная numFive, которая определена в функции, только внутри блока кода этой функции и "видна". В таких случаях говорят о локальной области видимости, хотя, может быть, более верным было бы даже название "локальная область существования" - ведь в действительности переменная, заданная в локальной области видимости, и существует только на Время выполнения этой функции - создаётся при вызове и уничтожается по окончании выполнения кода функции). Т.е. "за пределами" этой функции такой (правильно называемой локальной) переменной нет. Чтобы переменная была видна, она должна быть объявлена в той же области видимости - т.е., в данном случае, в основном таймлайне.
Одно из следствий такого положения дел - то, что можно задавать одни и те же имена переменным из разных областей видимости - они "не пересекаются".
Например:
CODE
var str:String = "String from main program";
function ex():Void {
var str:String = "String from function";
trace(str);
}

trace(str);
ex();
// Этот код корректно отработает и выведет
// в окошко вывода две разные строки
// Делать так - можно, но, конечно,
// следует избегать подобных конструкций -
// иначе трудновато будет разобраться.
// однако это очень удобно, когда, напр.,
// нужно задать несколько однотипных функций


Пример №2: здесь проблема в том, что переменная, определённая в таймлайне MovieClip2 - в другой области видимости, нежели те, что определены в основном таймлайне. "Внутри" ролика к таким переменным можно обращаться просто по имени, а вот снаружи - только "полным именем", т.е. - со всем путём от "корня иерархии" - _root.
Это возможно, когда ролику, лежащему на сцене, задано имя экземпляра - без этого обратиться к чему-либо, что у ролика "внутри", из основного таймлайна (_root-а) проблематично. Но если мы зададим в свойствах экземпляра такое имя - напр., "mc", то код в основном таймлайне исправляется на
CODE
var n2:Number = 2;
var n3:Number = n2 + _root.mc.n2;

- и всё в порядке.

Вообще, _root является корневым объектом, т.е. той областью видимости, в которой происходит выполнение основной программы (т.е. того кода, который находится в фреймах основного таймлайна). По отношению к вложенным роликам это - "корень иерархии роликов", т.е. "от корня" можно обратиться к любому из вложенных роликов "через точку"3, по отношению к переменным - "глобальная" область видимости.
Если мы пишем код на таймлайне вложенного ролика (не основном таймлайне) - то использование _root-синтаксиса ("полного пути") позволяет обратиться к переменным основного таймлайна или к другим, не вложенным роликам и их свойствам, а также определённым в них переменным.
Итак: если у нас есть MovieClip с именем, в таймлайне которого написан код, задающий переменную var1, и в основном таймлайне определена переменная var1, то:
- из кода, написанного в основном таймлайне, можно обратиться с помощью конструкции mc.var1 или _root.mc.var1
- из кода, написанного в таймлайне ролика, можно обратиться к переменной, определённой в этом таймлайне, с помощью выражения var14, а к переменной, заданной в основном таймлайне - с помощью _root.var1
Эти переменные друг другу "мешать не будут" - в каждой из них будет храниться своё независимое значение5

И ещё один момент в связи с этим примером: нужно помнить, что в Flash всегда есть маааленький подводный камушек - последовательность загрузки и выполнения. Если, например, попытаться выполнить исправленный Пример 2 в одном кадре - ничего не выйдет. Проблема будет в том, что код в основном таймлайне будет выполнен до того, как вложенный ролик на вообще появится на сцене - т.е. до того, как в нём будет определена переменная. Поэтому, если уж использовать код в таймлайнах роликов (чего вообще-то следует избегать когда и если только возможно) - то нужно учитывать, что их код будет выполнен только после полной загрузки и отображения первого кадра такого ролика. В данном примере, чтобы привести его к работоспособному виду, суммирование нужно будет вынести во 2-й фрейм основного таймлайна. При этом, поскольку нам нужен тот же самый экземпляр вложенного ролика, что и в первом фрейме - ключевые кадры для кода AS нужно вынести на отдельный слой основного таймлайна (впрочем, это нужно делать всегда), а на слое с вложенным роликом добавить обычный, не ключевой, кадр.

Пример 3:
Приведённый код будет прекрасно работать в ролике до тех пор, пока он "самостоятелен". Но как только мы попытаемся загрузить этот ролик в другой (напр., с помощью класса MovieClipLoader) - код работать перестаёт. Возможно, вы уже догадались, в чём проблема: то, что было _root-ом для "одиночного" ролика, после загрузки _root-ом быть перестало. Ведь обращение к _root из загруженного ролика - это обращение к "корню" загружающего ролика, а не самого загруженного!
Из этой ситуации есть два выхода:
1) если заранее известны все имена в загружающем и в загружаемом коде - использовать их. Это не универсально, но иногда можно и так, когда второй способ почему-либо не радует
2) использовать свойство _lockroot в загружающем ролике. Достаточно поставить в загружающем ролике строку this._lockroot = true; - и вуаля! - код внутри этого ролика всегда будет воспринимать ссылку на _root как ссылку на собственный рутовый таймлайн.
Адобовское руководство по AS2 рекомендует пореже использовать полные пути от _root вообще и _lockroot в частности. Вообще говоря, это правильно, и единственным оправданным применением _lockroot, как там и сказано, является случай, когда swf, который надо загрузить, есть, а fla-исходника от него - нету.
Однако я, видимо, ещё радикальнее, ибо считаю, что нигде, кроме основного таймлайна, а в идеале - только первого фрейма основного таймлайна, коду делать нечего. Очень мало есть вещей, которые действительно стоит писать В таймлайнах вложенных роликов, и к ним относятся разве что операторы stop(), gotoAndStop() и gotoAndPlay(). Обратное - путь к "макаронному", абсолютно нечитаемому коду и вообще way to hell6
Но иногда, в частности, когда события и функции вызываются "цепочкой", без _root-путей просто не обойтись.

3. Глобальная область видимости

Как вы уже, возможно, догадались, переменная, определённая в основном таймлайне, "видна", даже если мы "находимся в функции" - т.е. в тот момент, когда функция вызвана и исполняется её код. Если переменная "видна отовсюду", говорят, что она принадлежит к глобальной области видимости, а сама переменная называется глобальной.
Как же получается, что глобальная переменная "не пересекается" с локальной с тем же именем? - ведь если у глобальной и локальной переменной, одно и то же имя, то синтаксически внутри функции к ним и обращение одно и то же?
Вот здесь придётся немного разобраться в том, как флэш-плагин выполняет такие участки кода.
Для примера возьмём уже знакомый нам код:
CODE

// задаём глобальную переменную
var str:String = "global var";
// определяем функцию
function testLocalVar():Void {
var str:String = "local var"; // локальная переменная
trace(str); // при вызове функции выводим значение локальной переменной
}
// выводим значение глобальной переменной
trace(str);
// вызываем функцию:
testLocal();


После выполнения этого кода мы получим в окошке вывода:
global var
local var


Вопрос в том, почему при вызове функции у нас не вывелось значение глобальной переменной - ведь она из функции "видна". А объяснение тому очень простое: когда AVM7 выполняет вызов функции и в функции встречает имя переменной, то сначала это имя ищется среди объявленных локальных переменных. Если такое найдено - поиск прекращается, а если нет - то имя переменной ищется среди переменных глобальных8. И всегда только в такой последовательности.
Глобальную переменную можно задать и "изнутри" функции или из вложенного MovieClip-а с помощью свойства _global. Правда, и обращаться к ней нужно будет с помощью этого свойства. Например:

CODE
function setGlobalVarTest():Void {
_global.myVar = "global variable";
}
function useGlobalVarTest():Void {
_global.myVar += " OK";
}
//
setGlobalVarTest();
useGlobalVarTest();
trace(_global.myVar); // "global variable OK"


Как вы заметили, при задании глобальной переменной как свойства _global Тип этой переменной задать нельзя, т.е. эта переменная - неопределённого Типа. Следствие из этого - что её использование более "накладно" по части RAM. Зато её "видно" действительно "отовсюду" - и из функций, и из вложенных роликов, и даже из функций, определённых во вложенных роликах.

4. Функции и область видимости

С точки зрения областей видимости к функциям относится всё то же самое, что сказано об области видимости переменных. Функция также может быть задана глобально (как неименованная функция, присвоенная свойству _global, или как функция, независимо от "именованности", определённая в основном таймлайне) или локально (в коде таймлайна вложенного ролика). И вызов локально заданных функциям - вполне аналогичен обращению к локальной переменной. Пример:

CODE
// функция задана в таймлайне вложенного ролика с именем экземпляра "mc";
// Экземпляр ролика находится на основной начиная с фрейма 1:
function localFunctionTest():Number {
return 100*100;
}
// а этот код написан во втором кадре основного таймлайна:
var testNum:Number = mc.localFunctionTest();
trace("Результат: " + testNum);
// вывод: "Результат: 10000"


Если бы мы написали просто "localFunctionTest()", без "mc." - ничего бы не вышло, а компилятор выдал бы ошибку "функция не определена".
Особенное значение область видимости переменных и функций приобретает, когда мы работаем с классами. Там, когда переменные становятся свойствами, а функции - методами, для задания областей видимости есть специальные конструкции, определяющие, будет ли функция или переменная видна только внутри класса или не только, да и сам класс может быть "виден" по-разному... Но не будем забегать вперёд. Покончим сначала с глобальными функциями, тем более что среди них есть очень и очень полезные встроенные функции - т.е. готовые к употреблению и доступные отовсюду.

5. Встроенные глобальные функции

В AS2 многие часто встречающиеся действия выполняются с помощью т.н. "встроенных функций высшего уровня" (top-level built-in functions) - функций, определение которых задавать не нужно, поскольку об этом уже позаботились разработчики языка. Полный их список и описание действия вы можете посмотреть в Language Reference => Global Functions, я же опишу наиболее часто используемые:

setInterval(funcRef:Function, interval:Number, [arg1:Object, arg2,arg3,...argN]):Number - после того, как вызвана эта функция, функция, которая задана параметром funcRef, будет вызываться с аргументами, заданными параметрами arg1, ..., argN с периодичностью в interval миллисекунд.
эта функция возвращает число-идентификатор, само значение которого абсолютно никому не нужно, ибо единственное применение этого идентификатора - это возможность остановить именно этот периодический вызов с помощью встроенной глобальной функции clearInterval(intID:Number) - по этому числу будет идентифицирован именно этот периодический вызов. Напр.:

CODE
var count:Number = 0;
var maxCount:Number = 25;
var intID:Number;

function periodCount():Void {
count++;
trace("count is " + count);
if (count == maxCount-1) {
trace ("the end");
clearInterval(intID);
}
}
intID = setInterval(periodCount,1000);
// этот код выведет 25 строк с последовательными номерами от 0 до 25
// с интервалом в 1 секунду, после чего выведет строку "the end"
// и остановит периодический вызов функции.

При использовании этой функции нужно иметь в виду, что "обогнать FPS" её использование не позволит. Иначе говоря, её вызов со значениями интервала меньше, чем 1/ частоту кадров ролика, не даст, скорее всего, ничего, кроме путаницы - периодические вызовы синхронизированы по фреймрейту, и если AVM не успеет "провернуть" за один кадр несколько требуемых вызовов funcRef - то неразбериха обеспечена, особенно если используются какие-либо итеративные вычисления.

setTimeout() \ clearTimeout() - эти глобальные функции практически аналогичны setInterval() \ clearInterval() . разница в том, что второй параметр функции setTimeout() означает задержку выполнения функции, а не интервал, а функция clearTimeout() имеет смысл только до того, как этот интервал прошёл (и функция funcRef была вызвана).

getURL(url:String, [window:String, method:String]):Void - эта функция пытается открыть файл, находящийся по адресу, заданному первым параметром. Второй параметр может принимать занчения "_blank" (новое окно), "_self", "_parent" или "_top" или имени окна (если вы не знаете, что это - обратитесь к любому руководству по HTML\DHTML, где-то рядом с описанием тега FRAMESET). Параметр "method" задаёт метод передачи переменных, если таковые присутствуют в запросе - "GET" или "POST".
При вызове этой функции с параметром window равным "_self" выполнение скрипта в флэшке прекращается - что и понятно, страница, содержащая эту флэшку, уже перезагружается.

random(n:Number):Number - хотя эта функция и "не рекомендована к использованию", ибо ей на замену пришёл метод random() топ-левел класса Math - тем не менее, используется она очень часто. Она возвращает случайное целое число от 0 до n-1 включительно.

startDrag() / stopDrag() - эти функции реализуют механизм "перетаскивания" флэш-объектов-роликов, они описаны в уроке о событиях ( Основоположения программирования. Часть V.4 )

nextFrame() / prevFrame() - переход на следующий \ предыдущий фрейм текущей сцены при остановленном ролике.

nextScene() / prevScene() - переход на следующую \ предыдущую сцену.

gotoAndStop([scene:String], frame:Object) / gotoAndPlay ([scene:String],frame:Object) - переход и остановки / проигрывание соответственно с указанной сцены (если первый параметр задан; если он опущен - то в текущей сцене) на фрейм, указанный во втором параметре. Фрейм может указываться номером фрейма (Number)(не забывайте, что фреймы, в отличие от всего остального в программировании, нумеруются начиная с 1) или меткой (String)

play() / stop() - соответственно начало / остановка проигрывания ролика с / на текущем фрейме

parseFloat(str:String):Number - пытается превратить начало строки (или всю строку, если все символы строки - цифры) в число. Если первые символы строки представляют из себя число, в т.ч. отрицательное (т.е. начинающееся со знака "-") - то попытка будет успешной, и функция вернёт это число. В противном случае она вернёт специальное значение "NaN" (Not a Number), означающее, что в начале строки-аргумента нет числового значения.

updateAfterEvent() - вызов этой функции вызывает принудительное обновление дисплея (независимо от фреймрейта - если AVM "успевает", конечно). Используется в случаях, когда Изображение должно быть обновлено сразу же после выполнения каких-либо действий, например, в обработчиках событий.

Как видите, некоторые из глобальных функций - те, которые относятся к управлению проигрыванием основного таймлайна, а также и некоторые другие, напр., управляющие загрузкой \ уничтожением \ выгрузкой роликов - совпадают с методами объекта MovieClip. Это вполне естественно, потому что основной ролик является особенным, "главным", но тем не менее - роликом, MovieClip-ом9
Некоторые из остальных глобальных функций мы уже так или иначе использовали. Однако я очень рекомендую заглянуть в указанный раздел Language Reference - в AS2 глобальными функциями реализуются очень многие важные действия, от загрузки переменных до печати, и если не знать их наизусть, то хотя бы знать об их существовании с тем, чтобы в случае необходимости не изобретать велосипед, а сразу переходить на знакому страницу в руководстве по языку - очень невредно smile.gif

Продолжение следует
___________________________________
1 Особенно сейчас, когда большинство из того, что пишется с использованием пользовательских классов, пишется на AS3.
2 Фактически такая переменная является свойством экземпляра класса-наследника MovieClip. Ровно тот же эффект был бы, если бы мы задавали класс-наследник MovieClip и в нём - public variable n2:Number = 3.
3 Есть еще т.н. "слэш-синтаксис" - т.е. форма записи _root/inner1/inner2 , но она настолько не нужна и настолько, будучи наследством от AS1, устарела, что я не считаю нужным что-либо говорить о ней, кроме упоминания о её существовании, и то - лишь чтобы избегнуть упрёка в неупоминании оной smile.gif
4 Можно и "через задний двор" - т.е. конструкцией _root.mc.var1
5 Можно сказать и по-другому: у этих переменных разные имена, если считать именем вместе с полным путём: у одной - _root.var1, у другой - _root.mc.var1 Фактически флэш-плагин при выполнении ролика использует полные имена, вычисляя их по необходимости.
6 Поверьте, мне довелось наковыряться в исходниках флэш-шаблонов, и если и вправду тот, кого помянут прочувствованным словом, икает, то авторы кодов некоторых из этих "шедевров" не проикались и по сей день.
7 AVM - "виртуальная машина", т.е. та часть плагина, которая непосредственно "обслуживает" выполнение AS-кода и обеспечивает функциональность исполняемого флэш-файла (swf).
8 Если имя переменной и там не будет найдено - будет выдана ошибка "попытка использования необъявленной переменной". Эта ошибка относится к фатальным, т.е. выполнение флэш-программы будет остановлено.
9 В примечании, наверно, можно сказать, что с иерархией объектов и однозначно-точным ответом на вопрос "чем является основной документ в иерархии объектов" всё решилось только в AS3. В AS2 с этими вопросами всё весьма "рыхло".
Я не касаюсь здесь вопросов прототипирования в AS2, потому что в приложениях без пользовательских объектов прототипы не используются, да и в таковых их использование - более головная боль, нежели облегчение задачи. Также я здесь не ввожу понятия "_level", поскольку его использование нужно в считаных случаях, а вот вопросы областей видимости это запутает ещё больше и сделает вовсе неусваиваемыми. Мы вернёмся к этому по мере необходимости.

____________________________________


Автором урока является Des.
Запрещается копирование и публикация урока на других сайтах без письменного согласия автора и размещения ссылок.


The tutorial is written by Des.
No part of this tutorial can be copied/pasted on any other website without the author's express written permission.


Сообщение отредактировал Des - 25.08.2009 - 10:58


--------------------
"Высшая мудрость - умение разговаривать с людьми" ((с) Ямамото Цунэтомо (Дзётё), "Хагакурэ")
Вернуться в начало страницы
 
+Ответить с цитированием данного сообщения

Быстрый ответДобавить ответ в эту темуОткрыть тему
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 



- Текстовая версия форума Сейчас: 23.10.2017 - 09:24