задача: осознать, что такое "области видимости имен", "обращения к объектам", "ссылки на объекты" в ActionScript, и понять, зачем всё это нужно психически здоровому человеку.

Урок рассказывает, как можно "обращаться" к переменным и объектам, а так же разъясняет некоторые связанные с этим тонкости. Это урок для тех, кто только начинает понимать, что 90% флэша — это скрипт. Тем не менее, здесь не объясняются некоторые базовые понятия, вроде "что такое переменная?", "что такое функция?" и т. п. Если вы не знаете этого, обратитесь к другим справочным материалам.

назад к списку уроков и рецептов

1. что такое объекты (кратко)

2. что значит "обратиться к объекту"

3. что значит "путь к объекту"
    3.1 абсолютные и относительные пути
    3.2 сокращение пути
    3.3 составные пути, обращения при помощи квадратных скобок []

4. области видимости имён
    4.1  код в кадре
    4.2  код на клипе
    4.3  код на кнопке
    4.4  локальные переменные в коде функции
    4.5  _global и цепь областей видимости

5. искусственное изменение области видимости
    5.1  блок with(){}
    5.2  выполнение функций в заданной области видимости
    5.3  передача ссылки на объект в параметре функции
    5.4  передача ссылки на функцию многим объектам


+. как проверить, где переменные, куда идет обращение, и доступны ли объекты?



1. Что такое объекты

Всё есть объекты, как хорошо сказано в книге Thinking in Java. Весь мир состоит из объектов и их сочетаний. Для флэша это верно с небольшими оговорками.

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

2. Что значит "обратиться к объекту"

Чтобы получить какую-то информацию от человека, мы чаще всего обращаемся к нему по имени, и задаем интересующий вопрос. Человек обладает какой-то информацией, и эта информация изолирована от нас (пока никто не научился читать мысли), поэтому мы получаем доступ к данным, обращаясь к человеку.

С объектами дело обстоит точно так же: начинка каждого объекта (его свойства и методы) отгорожена от всего остального мира. Для того чтобы получить какое-то свойство объекта, или вызвать его метод, нужно обратиться к нему и вежливо об этом попросить. Для обращения к объекту во флэше служит оператор . (точка)
Выглядеть обращение может так:
имя_объекта.имя_свойства   или   имя_объекта.имя_метода()

в начало

3. Что значит "путь к объекту"

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

Так же дело обстоит и с объектами: они могут находиться "территориально" в разных местах, и не смогут обращаться друг к другу, не проследовав по пути от одного к другому. Путь во флэше составляется при помощи того же оператора "точка".

3.1 Абсолютные и относительные пути. Если предположить, что у нас в приложении воссоздана модель большого здания, где этажи и комнаты являются объектами, содержащими другие объекты, то обращение к свойству объекта А и путь к нему могли бы выглядеть так:
Улица.Здание.Этаж11.комната22.А.имя_свойства (или метода)
В таком случае путь будет абсолютным, потому что мы задали его начиная с самого главного, основного объекта "Улица".

Разумеется, путь можно было бы составить и относительно, например так:
Б.его_комната.её_этаж.следующий_этаж.комната22.A.имя_свойства (или метода)
 
В данном случае мы обращаемся к свойству объекта А из объекта Б, который находится в какой-то комнате на предыдущем относительно А этаже. "его_комната" означает "комната объекта Б", а "её_этаж" означает "этаж, на котором находится комната объекта Б". Иногда пользоваться относительными путями гораздо удобнее чем пользоваться абсолютными — это придает приложению гибкость. Хороший пример использования абсолютного пути при обращении к объектам — дом, который построил Джек.
Вот пёс без хвоста,
Который за шиворот треплет кота.
Который пугает и ловит синицу,
Которая ловко ворует пшеницу,
Которая в тёмном чулане хранится
В доме,
Который построил Джек

3.2 Сокращение пути. Представьте, что вы объясняете кому-то, как нужно нарядить ёлку в комнате на одном из этажей здания. Для начала вы рассказываете, как пройти в ту комнату, а потом уже говорите, какие украшения нужно повесить на ёлку, не упоминая каждый раз путь до комнаты. Это естественно.

При формировании обращений к объектам можно поступить точно так же. Можно сохранить путь до комнаты в переменной, а потом обращаться к объектам в комнате, используя эту переменную, а не полный путь. Например:
та_самая_комната = Улица.Здание.Этаж11.комната22
та_самая_комната.Ёлка
та_самая_комната.Коробка_с_украшениями
та_самая_комната.Мешок_с_подарками
То, что при этом хранится в переменной та_самая_комната, называется ссылкой на объект. Такое сокращение пути делает код понятнее, лаконичнее, и в итоге ускоряет его работу. Сокращать можно как абсолютные, так и относительные пути. Это очень полезная и часто используемая техника.

3.3 Составные пути, обращения при помощи квадратных скобок []. Возьмем ситуацию из предыдущего пункта: нужно объяснить, как пройти в комнату. Представьте, что вам нужно раздать задания по украшению ёлок десятерым. Скорее всего, вы скажете, в каком здании нужно наряжать ёлки, а потом по отдельности каждому — какой комнатой на каком этаже ему заняться.

То же самое можно сделать и при обращении к объектам. Допустим у нас есть несколько объектов, имеющих схожие имена, отличающиеся только индексом; например: комната1, комната2 и т. д. Часто бывает удобно составить при обращении к объекту его имя из кусочков. Выглядит это так:
Улица.Здание.Этаж11["комната"+22]
Обратите внимание на квадратные скобки и отсутствие уже привычного нам оператора . (точка) при обращении к комнате с составным именем. Общая форма такой записи:
имя_объекта [ выражение, из которого получается имя свойства/метода ]
Это значит, что мы можем поставить имя любого объекта, за ним поставить открывающую и закрывающую квадратные скобки, а между ними написать что угодно, лишь бы из этого получилось имя свойства или метода. Обратите внимание, что в предыдущем примере "этаж" и "комната" взяты в кавычки: этим я хочу подчеркнуть, что, внутри квадратных скобок должна получиться строка.

Самое интересное: мы можем комбинировать в одном пути и обращения через оператор . (точка) и через оператор []. Например, как бы выглядели обращения, если бы на каждом этаже была комната 22:
Улица.Здание["Этаж"+11].комната22
Улица.Здание["Этаж"+12].комната22
Улица.Здание["Этаж"+13].комната22
Чем это удобно? Тем, что внутри квадратных скобок могут быть выражения, содержащие переменные, вызовы функций, и вообще всё что угодно. Предыдущий код мы могли бы переделать так:
for (var i=11; i<=13; i++) { // для i от 11 до 13
    // обращаемся к комнате22 на этаже i
    Улица.Здание["Этаж"+i].комната22
}
Заметьте, после закрывающей квадратной скобки стоит оператор . (точка). Общее правило таково: если имя нужно составить из кусочков, то мы ставим не точку, а квадратные скобки; если же у нас есть готовый идентификатор, мы ставим точку, даже если до этого стоит закрывающая квадратная скобка. То есть, можно написать и так, например:
Улица["Здание"+1].Этаж11["комната"+22].Мешок_с_подарками


в начало

4. Области видимости имён

Итак, мы знаем что такое объекты, и как к ним можно обратиться. При чем же собственно "области видимости имен"? Один из случаев, когда мы столкнулись с областями видимости (слышимости) имён был рассмотрен в первом абзаце предыдущего пункта. Объекты в разных комнатах на разных этажах здания не видят и не слышат друг друга. В целом это и есть общая суть понятия "областей видимости".

При написании кода во флэше есть несколько тонкостей, на которые необходимо обратить внимание.

При любом использовании имени переменной/функции следует подумать:

   а) в какой области видимости находится блок кода, откуда идет обращение
   б) в какую область обращение должно попасть


Пожалуй, это центральная мысль данного материала. Код во флэше может быть написан в нескольких местах: в кадре, на клипе, на кнопке, в классе. Рассмотрим пока что первые три случая.

4.1 Код находится в кадре. Выделяете любой кадр, открываете панель скриптов и пишете там. Это означает что все переменные и функции, которые вы определите в этом блоке кода, будут находиться в области видимости клипа, в кадре которого вы пишете. Если вы пишете на главной линейке кадров (или на главной временной диаграмме, как её часто называют в литературе), то переменные и функции, определенные этим кодом, будут находиться в области видимости _root. Не забывайте, что _root — тоже мувиклип, хоть и особенный.

Все переменные и функции, созданные в коде какого-то кадра клипа, доступны в любом другом кадре этого клипа. Стоит только помнить, что код в кадре срабатывает только тогда, когда кадр проигрывается. Соответственно, если мы создадим переменные в коде кадра 10, то в коде кадра 1 они не будут доступны до тех пор пока кадр 10 хотя бы один раз не проиграется.

Пример:
var a = 1;
var b = function ()
{
};
var c = "это переменная, созданная в коде первого кадра _root";
Переменные находятся в области видимости клипа, значит, если мы попытаемся обратиться к какой-то переменной (объекту), не указав явно (с помощью пути), откуда её взять, то будет считаться, что мы пытаемся получить переменную из клипа, в котором написан этот код.

Например:
a_object = {}; // создали объект
a_object.property = "это свойство"; // создали в нём свойство
a_object.method = function() // создали в нём метод
{
    trace(property); // выводим в Output значение свойства property
};
a_object.method(); // вызываем метод

 
В результате выполнения метода в окошке Output появится:
undefined
Почему? Ведь в объекте a_object, которому принадлежит метод, есть свойство с таким именем! Дело в том, что мы не указали явно, откуда взять свойство property. И флэш попытался найти его в том клипе, в кадре которого написан код, а вовсе не в a_object. Попробуем создать переменную property там же, где создали объект a_object:
property = "это переменная!"; // создали переменную
a_object = {}; // создали объект
a_object.property = "это свойство"; // создали в нём свойство
a_object.method = function() // создали в нём метод
{
    trace(property); // выводим в Output значение свойства property
};
a_object.method(); // вызываем метод

 
Выводится:
это переменная!
Видим, что была взята переменная с именем property из области видимости клипа, а не из области видимости объекта. Значит, надо каким-то образом задать путь к свойству property. Первое, что может придти в голову, это написать a_object.property... Действительно, в данном случае это будет работать, поскольку идентификатор a_object (имя объекта) сам тоже находится в области видимости клипа, в котором написан код.
property = "это переменная!"; // создали переменную
a_object = {}; // создали объект
a_object.property = "это свойство"; // создали в нём свойство
a_object.method = function() // создали в нём метод
{
    // выводим в Output значение свойства property
    trace(a_object.property); 
};
a_object.method(); // вызываем метод

 
Выводится:
это свойство
Работает! Однако это плохой стиль, делающий код менее гибким и потенциально глючным. И вот почему:
property = "это переменная!"; // создали переменную
b_object = {}; // создали объект
b_object.a_object = {}; // создали еще один объект внутри первого объекта
b_object.a_object.property = "это свойство"; // создали в нём свойство
b_object.a_object.method = function() // создали в нём метод
{
    // выводим в Output значение свойства property
    trace(a_object.property); 
};
b_object.a_object.method(); // вызываем метод

 
Выводится:
undefined
Несмотря на то, что весь код написан в кадре одного клипа, идентификатор a_object в область видимости этого клипа не попадает! Это логично, ведь мы специально создали a_object внутри b_object. То есть теперь, чтобы обратиться к a_object.property нам придется написать b_object.a_object.property... Но давайте не будем повторять судьбу Джека, строившего дом. Есть способ лучше: написать внутри метода "своё.property". Тогда метод, выполняющийся из объекта a_object, будет знать, что нужно взять именно своё свойство, где бы при этом сам объект ни находился. Для этого используется ключевое слово this.
property = "это переменная!"; // создали переменную
b_object = {}; // создали объект
b_object.a_object = {}; // создали еще один объект внутри первого объекта
b_object.a_object.property = "это свойство"; // создали в нём свойство
b_object.a_object.method = function() // создали в нём метод
{
    // выводим в Output значение свойства property
    trace(this.property); 
};
b_object.a_object.method(); // вызываем метод

 
Выводится:
это свойство
Мы использовали ключевое слово this в значении "своё" или "у меня". Таким образом было взято свойство у того же объекта, в котором выполняется метод, а не у их общей области видимости. Иногда, при использовании искусственного изменения области видимости имён, может быть непонятно, что же данная функция считает "своим" объектом. Бывают ситуации, когда функция вообще не знает, какой объект считать "своим", как например в случаях с setInterval. Для того чтобы увидеть, куда ссылается this в функции, достаточно воспользоваться trace(this);.

4.2 Код находится на клипе. Это значит, что мы написали код внутри обработчика onClipEvent(){} или on(){} на самом клипе (выделяете на сцене клип, открываете панель скриптов — и пишете там). Код на клипе всегда является реакцией на какое-то событие, будь то смена кадров, движение мышки и т.п. В остальном этот код равноценен коду, написанному в кадре клипа. То есть, для всех переменных и функций, определенных в коде на клипе, областью видимости имён является этот клип.

Пример:
on (press) {
    // код срабатывает при нажатии на этот клип
    //
    // этот код может быть написан на клипе или кнопке,
    // попытка написать его в кадре приведет к ошибке!
    //
    // в данном случае считаем, что это написано на КЛИПЕ
    a = 1; // создали переменную, которая теперь будет находиться в ЭТОМ клипе
    trace(a); // вывели её значение в Output
}

 
Обратите внимание: мы не указали, где создаем переменную, и не указали откуда её взять, чтобы вывести в Output. Областью видимости имён для этого кода является клип, на котором мы всё это написали, поэтому при обращении к переменной без указания пути к ней, мы получаем переменную из этого клипа. Создайте клип, повесте на него этот код, запустите ролик, щелкните по клипу левой кнопкой мыши и проверьте, где создалась переменная при помощи команды List Variables.

4.3 Код находится на кнопке. Это значит что мы написали код внутри обработчика on(){} на кнопке (выделяете на сцене кнопку, открываете панель скриптов и пишете там). Код на клипе всегда является реакцией на какое-то событие, будь то смена кадров, движение мышки и т.п. Однако область видимости для функций и переменных, которые вы создадите в коде на кнопке, отличается от той, что получается для кода на клипе. Исторически сложилось так, что код обработчика on(){} на кнопке работает так, будто кнопка не является объектом вовсе, и не имеет собственной области видимости. Областью видимости для кода на кнопке является её родительский клип, т.е. тот клип, в кадр которого вложена эта кнопка.

Пример:
on (press) {
    // код срабатывает при нажатии на эту кнопку
    //
    // этот код может быть написан на клипе или кнопке,
    // попытка написать его в кадре приведет к ошибке!
    //
    // в данном случае считаем, что это написано на КНОПКЕ
    a = 1; // создали переменную, 
           // которая теперь будет находиться в РОДИТЕЛЬСКОМ клипе!
    trace(a); // вывели её значение в Output
}
Обратите внимание: мы не указали, где создаем переменную, и не указали откуда её взять, чтобы вывести в Output. Областью видимости имён для этого кода является родительский клип, в кадр которого вложена кнопка, поэтому при обращении к переменной без указания пути к ней, мы получаем переменную из родительского клипа. Создайте кнопку, повесте на неё этот код, запустите ролик, щелкните по кнопке левой кнопкой мыши и проверьте, где создалась переменная при помощи команды List Variables.

4.4 Локальные переменные в коде функции. Переменные во флэше можно создавать просто присвоив значение какому-то идентификатору. Если переменная с указанным идентификатором (и путем) уже существет — её значение изменится. Если такой переменной еще нет — она будет создана.

Для удобства структурирования программ существует ключевое слово var. Если мы объявляем переменную с ключевым словом var, это значит, что мы хотим сделать её локальной для текущего блока кода. Понятие "локальности" зависит от того, где находится блок кода, в котором происходит создание переменной.

Если ключевое слово var используется в коде кадра, в коде клипа клипа, или коде кнопки - эффект получается точно таким же, как и при задании значения переменной без var. В этом случае отличий по области видимости имён нет.

Пример:
on (press) {
    // в данном случае считаем, что это написано на КЛИПЕ
    a = 1; // создали переменную, которая теперь будет находиться в ЭТОМ клипе
    trace(a); // вывели её значение в Output
    var b = 1; // создали переменную, которая тоже теперь будет находиться в ЭТОМ клипе
    trace(b); // вывели её значение в Output
}
 
Убедитесь при помощи List Variables, что никакой разницы в расположении переменных "a" и "b" нет.

Если ключевое слово var используется в коде функции, то переменная становится локальной для этой функции и вдобавок к этому временной: когда функция прекращает свою работу, эта переменная перестает существовать. В этом основное и главное назначение var.

Пример:
test_function = function ()
{
    var a = "это переменная локальная для функции test_function";
    trace(a);
};
test_function();
trace(a);
Выводится:
это переменная локальная для функции test_function
undefined
Первая строчка — это значение локальной переменной "a". Вторая строчка — undefined, поскольку такой переменной в текущей области видимости нет. Переменная "a" создается в функции test_function и исчезает сразу по завершении её работы. При помощи List Variables вы можете убедиться, что это действительно так. Область видимости этой переменной ограничивается кодом функции test_function, а время жизни — временем выполнения test_function. Подчеркиваю, такая область видимости характерна только для переменных, объявленных с var внутри функции. В случаях c onClipEvent(){} или on(){} var никакой роли для областей видимости не играет.

Есть, правда, некоторые случаи, когда локальная переменная функции не исчезает после завершения работы функции. И в стиле AS2.0 var используется для задания типизированных переменных, а не только для задания временных переменных в функции. Но это уже другая история...

4.5 _global и цепь областей видимости. В каждом ролике существет объект _global. Всё содержимое этого объекта доступно без указания пути в любой точке ролика и из любого объекта. Кстати, ссылки на конструкторы стандартных классов и на некоторые предопределенные функции находятся в объекте _global. Именно благодаря этому можно, к примеру, написать в любой части ролика:
trace("сообщение");
И получить сообщение в Output. Дело в том, что ссылка на функцию trace() записана в _global, и к ней можно обратиться, не указывая пути. Впрочем, путь можно и указать — тогда мы убедимся, что функция trace действительно присутствует в _global.
_global.trace("вызов trace() из глобала");
Всё это означает, что определив в объекте _global свойство _global.property, мы можем получить это свойство в любом клипе и в любом методе, просто написав "property". Простой пример:
_global.test = "это тестовое свойство объекта _global";
trace(test);
В Output:
это тестовое свойство объекта _global
Казалось бы, мы записали свойство в объект, однако оно вывелось и без указания пути к нему. Таково индивидуальное свойство объекта _global.

Однако, это не панацея. Не стоит всё подряд складывать в _global ради того, чтобы не задумываться о путях к объектам и переменным. Так можно нарушить структуру программы и окончательно всё запутать. К тому же, с _global связана еще одна особенность: этот объект является самой внешней областью видимости.

При обращении к чему бы то ни было вы автоматически задействуете поиск в цепочке областей видимости. Например, как показано в пункте 5.1, в случае, если запрашиваемое свойство не находится в области видимости блока with(), оно автоматически берется из более внешней области видимости. Такая же схема работает везде, где вы запрашиваете нечто, не указывая точно, откуда это нечто взять. И объект _global в цепочке областей видимости находится самым последним.

Запутанный пример:
// создаем свойство в _глобал
_global.property = "свойство, записаное в объект _глобал";

// а теперь свойство с таким же именем в клипе, где написан весь этот код
property = "свойство, записанное в текущий клип";

a_object = {}; // создаем объект и свойство в нем
a_object.property = "свойство, записаное в объект A";

a_object.test_function = function() // создаем метод
{
    // и временную переменную в нем
    var property = "временная локальная переменная функции";
    /*
    красота! 
    теперь у нас есть три свойства и одна переменная,
    которые мирно сосуществуют, 
    но при этом все называются одинаковыми именами
    */
    trace(this.property); // попробуем вывести свойство объекта...
    trace(property); // а теперь посмотрим что выведется, 
                     // если не указать путь к свойству
};
// вызываем метод, чтобы оценить масштаб катастрофы
a_object.test_function();

 
В Output видим:
свойство, записаное в объект A
временная локальная переменная функции
Аха. С первым всё понятно: мы указали путь — this — и результат вполне ожидаемый. А во втором случае флэш воспользовался поиском по цепочке областей видимости, чтобы определить, что же следует вывести. Первое, что флэш просмотрел в поисках переменной с именем property, была локальная область видимости функции. Переменная property тут же нашлась и на этом поиск прекратился.

Теперь упростим пример, уберем локальную переменную функции:
// создаем свойство в _глобал
_global.property = "свойство, записаное в объект _глобал";

// а теперь свойство с таким же именем в клипе, где написан весь этот код
property = "свойство, записанное в текущий клип";

a_object = {}; // создаем объект и свойство в нем
a_object.property = "свойство, записаное в объект A";

a_object.test_function = function() // создаем метод
{
	trace(property); // посмотрим что выведется, 
	                 // если не указать путь к свойству
};
a_object.test_function();

 
Видим в Output:
свойство, записанное в текущий клип
Всё произошло так, как было описано в пункте 4.1. Но теперь мы знаем как именно это происходит: флэш стал искать переменную с именем property, перебирая области видимости. Сначала была просмотрена локальная область видимости функции, затем внешняя для функции. И во внешней области — в клипе, в кадре которого написан этот код — переменная была найдена.

Сделаем пример еще проще, удалив свойство клипа:
// создаем свойство в _глобал
_global.property = "свойство, записаное в объект _глобал";
a_object = {}; // создаем объект и свойство в нем
a_object.property = "свойство, записаное в объект A";
a_object.test_function = function() // создаем метод
{
	trace(property); // посмотрим что выведется, 
	                 // если не указать путь к свойству
};
a_object.test_function();
В Output:
свойство, записаное в объект _глобал
Теперь, при поиске свойства property в цепочке областей видимости, флэш нигде на него не наткнулся. Дойдя до самой внешней области видимости, до объекта _global, флэш всё-таки свойство обнаружил и вывел.
в начало

5. Искусственное изменение области видимости

Было бы очень неудобно, если бы нельзя было самому задавать область видимости для выполнения какого-то кода. Часто гораздо легче и удобнее заставить код выполниться именно в той области, в которой вы хотите, а не продумывать куда попадет обращение если я... К тому же это делает код более понятным и структурированным. Существует несколько способов задавать область видимости для кода: блоки with(){}, методы Function.apply и Function.call. Существуют также блоки tellTarget(){}, но эта конструкция считается устаревшей уже с 6 версии флэша, и поэтому её описания в этом материале нет.

5.1 Блок with(). Иногда требуется явно задать область видимости для нескольких операций сразу. Для этого существует конструкция with(){}. В принципе её назначение можно описать просто: написав with(имя_объекта) мы говорим что все дальнейшие операции производятся относительно указанного объекта. Т.е. обращаясь внутри блока with() к переменным без указания пути к ним, мы дожны получать их из указанного в with() объекта.

Пример:
a_object = {};
a_object.a_property = 2;
a_object.b_property = 2;
with (a_object) {
    trace("сумма = "+(a_property+b_property));
    trace("произведение = "+(a_property*b_property));
}
Выводится:
сумма = 4
произведение = 4
Внутри блока with() нам не приходится каждый раз указывать, что свойства a_property и b_property нужно брать у объекта a_object. Это улучшает читаемость кода, уменьшает его размер после компиляции, и "теоретически" должно ускорять его работу.

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

Например, если мы напишем в кадре:
a_object = {}; // создали объект
with (a_object) { // для объекта a_object...
    property = "свойство"; // создаем свойство
}
trace(a_object.property); // которое, как выясняется, находится не в объекте

 
В Output мы получим:
undefined
Свойство создалось не в объекте, а в клипе, в кадре которого этот код написан. При помощи List Variables вы можете убедиться, что это так. А всё потому, что флэш, не найдя в объекте a_object свойства property, автоматически определил область видимости, как внешнюю относительно with(). А если, скажем, использовать блок with() внутри функции, то нужно быть очень внимательными, потому что:
— сначала флэш проверит, нет ли такого свойтсва в указанном объекте
— в случае неудачи попытается найти его во временных переменных функции
— и в случае неудачи будет искать во внешней для функции области видимости.
Здесь довольно легко запутаться. По этой пичине я редко пользуюсь такой конструкцией, и предпочитаю технику сокращения пути.

В общем, блок with() пригоден для группировки нескольких операций с уже существующими свойствами или методами объекта. Типичный пример использования:
// создадим пустой клип
this.createEmptyMovieClip("square", 0);
square._x = 100;
square._y = 100;
// нарисуем в нём квадрат
with (square) {
    // дальнейшие действия относятся к клипу square
    lineStyle(3, 0x00cc00, 100);
    moveTo(-100, -100);
    lineTo(100, -100);
    lineTo(100, 100);
    lineTo(-100, 100);
    lineTo(-100, -100);
}
Здесь мы несколько раз подряд пользуемся тремя методами клипа square: lineStyle, moveTo и lineTo, и нам не приходится каждый раз указывать клип square в качестве пути к этим методам. Использование with() здесь оправдано, потому что мы не создаем никаких переменных и не производим никаких действий с неясной областью видимости имён.

5.2 Выполнение функций в заданной области видимости. Есть набор действий, который нужно выполнить с определенным объектом. Например, наряжая ёлку, мы поместим наверху звезду, потом развесим шарики, потом обмотаем всё получившееся гирляндами. А теперь представьте, что у нас в каждой комнате здания стоит по ёлке, и каждую нужно нарядить. Мы можем один раз составить список действий для одной ёлки, а потом выполнить их и для всех остальных.

Список действий — это метод. Предположим мы создали этот метод в одном объекте, и он использует ключевое слово this, для управления свойствами "собственного" объекта. В примере к пункту 4.1 было показано использование ключевого слова this, хранящего ссылку на объект, в котором выполняется метод. А что если мы хотим применить метод к другому объекту, ничего в этом методе не переписывая? Это возможно, благодаря методам функции call() и apply(). И это не тавтология: функции — это тоже объекты, хотя и специфические.

Метод Function.call(какой_то_объект) — буквально "вызвать" — позволяет вызвать любую функцию, как будто она является методом объекта какой_то_объект. Метод Function.apply(какой_то_объект) — буквально "применить" — позволяет применить любую функцию к объекту какой_то_объект. Разница между этими двумя вариантами только в том, как передаются параметры в вызываемую функцию.

Пример:
a_object = {}; // создали объект
a_object.property = "это свойство объекта _A_!"; // создали в нём свойство
a_object.method = function(text) // создали в нём метод
{
    trace(text); // выводим в Output переменную text из параметров
    trace(this.property); // выводим собственное свойство property
};
// вызываем метод обычным способом, 
// из того объекта, в который мы его изначально поместили
a_object.method("\tметод вызван из объекта А");
//
b_object = {}; // создали еще один объект
b_object.property = "это свойство объекта _Б_!"; // создали в нём свойство
// вызываем метод объекта a_object для объекта b_object
a_object.method.apply(b_object, ["\tметод вызван для объекта Б"]);
// еще раз, другим способом
a_object.method.call(b_object, "\tметод вызван для объекта Б");

 
Выделенные жирным шрифтом строчки показывают, как можно вызвать метод объекта a_object относительно b_object. В Output видим:
    метод вызван из объекта А
это свойство объекта _A_!
    метод вызван для объекта Б
это свойство объекта _Б_!
    метод вызван для объекта Б
это свойство объекта _Б_!
Несмотря на то, что в методе использовано обращение this.property, а сам метод находится в объекте a_object, свойство выводится из объекта b_object. То есть, метод при вызове уверен, что расположен в b_object. Обратите внимание, что мы вызываем методы call() и apply() у функции a_object.method, то есть обращаемся с ней как с объектом. (Узнать больше о синтаксисе методов call() и apply() вы можете на novemberain.com здесь и здесь соответственно.)

5.3 Передача ссылки на объект в параметре функции. Как было показано в предыдущем пункте, мы можем вызвать метод так, чтобы он считал "своим" тот объект, который мы захотим, в не тот, в который он изначально записан. Такого же по сути эффекта можно добиться, слегка переделав механизм работы функции. В пункте 3.2 упоминалась техника использования ссылки на объект для сокращения пути. Мы можем использовать ссылку и для того, чтобы сообщить функции, с каким объектом ей следует работать. Ссылка позволяет работать с объектом так, будто вы написали полный и абсолютный путь к нему.
test_function = function (reference, text)
{
    // записали в объект, на который ссылается reference,
    // свойство message со значением переменной text
    reference.message = text;
    //
    // выводим в Output свойство property объекта, 
    // на который ссылается reference	
    trace(reference.property); 
};
a_object = {}; // создали объект
a_object.property = "это свойство объекта А";
b_object = {}; // создали еще один объект
b_object.property = "это свойство объекта Б";
// вызываем test_function, передавая ей ссылку на объект a_object
test_function(a_object, "это сообщение в объекте А");
// вызываем test_function, передавая ей ссылку на объект b_object
test_function(b_object, "это сообщение в объекте Б");
В Output видим:
это свойство объекта А
это свойство объекта Б
А, воспользовавшись List Variables, видим:
Variable _level0.a_object = [object #2, class 'Object'] {
    property:"это свойство объекта А",
    message:"это сообщение в объекте А"
  }
Variable _level0.b_object = [object #3, class 'Object'] {
    property:"это свойство объекта Б",
    message:"это сообщение в объекте Б"
  }
И таким образом убеждаемся, что свойство message действительно записалось через ссылку в оба эти объекта.

5.4 Передача ссылки на функцию многим объектам. Как было показано в пункте 5.2, функция — это тоже объект, и с ней можно обращаться как и со всеми прочими объектами. А раз функция — объект, то на неё есть ссылка. В предыдущем пункте мы увидели, как можно использовать ссылку на объект, чтобы сообщать функции, над каким объектом ей следует потрудиться. Часто бывает удобнее сделать наоборот: раздать нескольким объектам ссылку на функцию, которой им следует пользоваться. Мы могли бы запросто создать по функции в каждом объекте, но когда нам везде нужны одни и те же действия — раздать объектам ссылки на одну функцию гораздо удобнее. Это делает программу логичнее, облегчает её поддержку и отладку.
spin = function ()
{
    // функция работает с объектом через ссылку this
    //
    // вращаем
    this.angle += this.spin;
    this._rotation = this.angle;
};
for (var i = 0; i<30; i++) {
    // создаём мувиклип (объект типа MovieClip).
    // метод createEmptyMovieClip() возвращает ссылку,
    // через которую мы работаем с клипом
    var point = this.createEmptyMovieClip("point"+i, i);
    point._x = 100;
    point._y = 100;
    var radius = 10+i; // радиус вращения
    with (point) {
        // рисуем в клипе закорючку
        /* обратите внимание, переменной radius нет в объекте point
           однако её значение автоматически находится во внешней
           для этого блока with() области видимости */
        lineStyle(4, 0, 100);
        moveTo(radius, 0);
        lineTo(radius, 1);
        lineTo(radius-1, 1);
    }
    // записываем в объект point ссылку на функцию followMouse
    // под именем onEnterFrame
    point.onEnterFrame = spin;
    // создаем свойства, управляющие вращением
    point.spin = i/3; // скорость вращения
    point.angle = 0;  // изначальный угол поворота
}
Мы создаем в цикле 30 клипов и всем им даем ссылку на функцию spin в качестве обработчика onEnterFrame. Это избавляет нас от необходимости создавать одну и ту же функцию 30 раз. Результат работы этого кода (посмотреть отдельно):


в начало

+. Как проверить, где переменные, куда идет обращение, и доступны ли объекты?

1.
Запустите ролик во флэш-редакторе (Test Movie, Ctrl+Enter). В верхнем меню выберите Debug>List variables, и вы увидите окошко, содержащие список всех объектов и переменных, которые на данный момент существуют в вашем ролике (при стандартных настройках это действие вызывается сочетанием Ctrl+Alt+V). Это самый простой способ посмотреть где что.

2.
Второй основной способ — пользоваться функцией trace(). Бóльшая часть отладки происходит при помощи этой чудо-функции, поскольку на данном этапе развития флэш дебаггер неудобен, не всегда работает и т п. trace всего лишь выводит в окошко Output то, что вы ей дадите в качестве аргумента. Это окошко можно увидеть только при запуске ролика во флэш редакторе. Трэйсить можно всё что угодно: объекты, функции, переменные... Благодаря функции trace вы сможете определить, что, во-первых, код выполнился (это иногда бывает совсем не очевидно), во-вторых — посмотреть текущие значения переменных, в третьих — убедиться, что объект или функция доступны так, как вы к ним пытаетесь обращаться. Например:
var a = 1;
var b = {};
var c = function ()
{
    a += 1;
    trace(a);
    trace(b);
    trace(d);
};
c();
В результате выполнения этого кода в окошке Output появляется:
2
[object Object]
undefined
Ага, говорим мы, 2 — это новое значение переменной "a" после того, как мы увеличили её на единицу. Дальше [object Object] — это не слишком осмысленная конструкция, но зато мы видим, что "b" — это какой-то объект и он в этом коде доступен. Дальше undefined — это говорит нам о том, что переменная "d" не доступна в этом коде: либо её вообще не существует, либо она находится в другой области видимости.

3.
Третий способ — воспользоваться дебаггером. Это не удобно, а что делать?Иногда помогает. Запустить дебаггер можно через пункт меню Control>Debug movie или стандартным сочетанием Ctrl+Shift+Enter. Но большое приложение, например, может просто не запуститься через дебаггер.


в начало
назад к списку уроков и рецептов


Корректировать материал помогли: