Нам,
В этом уроке, программируя приложение
В этом уроке:

В этом уроке мы покажем вам, как работают объекты и как их создавать, так что впоследствии вы сможете строить новые
классы объектов с нуля, равно как и расширять возможности существующих.
На выполнение этого урока требуется примерно два с половиной часа.
Файлы-носители:
Нет
Стартовые файлы:
Lesson06/Assets/object1.fla Lesson06/Assets/class1.fla Lesson06/Assets/objectProject1.fla Lesson06/Assets/registerClass1.fla
Законченные проекты:
object2.fla class4.fla objectProject4.fla registerClass2.fla
В Уроке 4 мы познакомили вас со встроенными объектами
Чувствую, сейчас кто-нибудь скажет: "Мы же уже создавали объекты и модифицировали их в четвертом уроке, теперь что, опять то же самое?" Да, в Уроке 4 вы создавали экземпляры Array, Color, Date, String, и т.д.), но мы не говорили ни о том, как создать свой собственный
Примечание Экземпляры объектов часто называют просто объектами. Как правило, говоря об объектах, подразумевают именно экземпляры.
Концепция построения и работы объектов есть вещь довольно-таки абстрактная, поэтому упражнения в нескольких следующих разделах будут несколько отличаться от прочих. В большинстве упражнений из этой книги вы имеете дело с вещами зримыми и осязаемыми – обычно это графические элементы, которыми вы манипулируете в проекте. В следующих упражнениях действие будет происходить по большей части за сценой. Здесь нам придется пользоваться действием
trace(_root._totalframes);
Если это действие поместить в первый кадр фильма, состоящего из 47 кадров, то при тестировании фильма автоматически откроется окно Выход ( Output ) и в нем отобразится:
47

Это простое действие очень пригодится нам при изучении работы объектов.
Что ж, начнем.
Примечание Если вы знакомы с
Объекты
person1 = new Object();
Эта строка создает новый экземпляр родового объекта с именем person1.
Примечание Объект Object (использованный здесь) не имеет специального применения – это родовой объект. Таким образом, в приведенной строке фигурирует экземпляр объекта Object (а не новый класс), который мы создали, чтобы продемонстрировать роль объекта как контейнера. В следующем уроке вы узнаете, что объект Object имеет более широкое применение.
На данный момент person1 – никакая не персона, вообще никто: мы не задали для нее никаких характеристик – ни пола, ни возраста, вообще ничего.
person1.gender = "male"; trace (person1.gender); trace (person1.age);
Теперь объект person1 имеет свойство, под названием gender, со значением "male". Еще мы добавили пару действий , чтобы посмотреть, какие значения имеются в объекте person1. Давайте посмотрим, что у нас есть на данный момент.
Откроется окно Выход (Output), и в нем отобразится:
male undefined
Первое из действий вернуло значение "male", поскольку именно такое значение имеет person1.gender. Второе действие вернуло значение для person1.age – это значение мы не присвоили, оно не определено.
Давайте добавим в person1 еще несколько свойств и значений.
person1 = new Object(); person1.age = 16; person1.legs = 2; person1.name = "Justin";
Как видим, person1 приобретает все больше черт. Теперь она имеет все самое необходимое, чтобы войти в общество.

На данный момент любой скрипт может получить доступ к данным, связанным с person1 – мы продемонстрируем это на следующем шаге.
if (person1.age >= 21){
trace("Я иду на дискотеку");
}else{
trace("Я иду в МакДональдс");
}
Этот оператор проверяет значение person1.age, чтобы определить дальнейший образ действий.
Откроется окно Выход (Output), и в нем отобразится:
Я иду в МакДональдс
Оператор if выдает именно такой результат, поскольку person1.age имеет значение 16.
Давайте добавим еще кое-что в наш объект person1 – продемонстрируем, что объект может содержать в себе другой объект. Эта концепция вам, в принципе, знакома – вспомните вложенные монтажные столы (это ведь не что иное, как объект внутри другого объекта ).
person1.head = new Object(); person1.head.eyes = 2; person1.head.memories = new Array();
Итак, наша персона person1 получила (наконец-то!) голову – head. Это произошло в результате создания нового экземпляра объекта Object и помещения его внутрь объекта person1. Объекту head мы дали свойство eyes, имеющее значение 2. Мало того, внутрь объекта head мы поместили объект Array (c именем ).

Как видите, мы поместили один объект ( ) внутрь другого объекта ( head ), который, в свою очередь, находится в объекте person1. Мы могли бы поместить непосредственно в person1, вот так:
person1.memories = new Array();
Мы так не поступили, и на то были веские причины, о которых мы скоро расскажем.
Совет Можно поместить в person1.head существующий массив – с именем, скажем, myThoughts (а не создавать новый, как было показано), для этого следовало бы использовать такой синтаксис: person1.head..
Что ж, давайте добавим несколько воспоминаний в массив .
person1.head.memories[0] = "Я родился, пришел в этот мир."; person1.head.memories[1] = "Я узнал слово 'НЕТ'. Очень продуктивный день."; person1.head.memories[2] = "Я узнал, что если малость поору, то получу, что хочу.";
Эти три строки помещают данные в массив , находящийся внутри head объекта person1.
trace(person1.head.memories[1]);
Откроется окно Выход (Output), и в нем отобразится такая надпись:
Я узнал слово 'НЕТ'. Очень продуктивный день.
Это – значение, которое содержит person1.head..
Все, что мы запрограммировали, мы используем в следующем упражнении. Пока что запомните, что объекты – это контейнеры, способные хранить не только простые "порции" данных, но даже другие объекты. Смысл тут тот же, что при помещении переменной в экземпляр фильма-символа, или при создании на монтажном столе экземпляра Color, или другого объекта (см. Урок 4 – "Объекты и их использование"). Вы помещаете объект (скажем, экземпляр объекта Color ) на монтажный стол (внутрь экземпляра объекта ). Создавая на монтажном столе переменную, вы на самом-то деле даете новое свойство объекту .
Когда вы помещаете монтажный стол (то есть, экземпляр объекта ) на другой монтажный стол (другой экземпляр) – вам приходилось делать это с фильмами-символами – вы создаете
person1.head.memories
Здесь head является дочерним объектом по отношению к person1, а – head, и person1 (таким образом, head – родитель , а person1 – предок head и ).
Теперь внимание: следует помнить о том, что если что-то происходит с родителем, то же случается и с его потомками – что мы и продемонстрируем в следующем упражнении.
delete person1.head; trace(person1.head); trace(person1.head.memories[1]); trace(person1.name);
Первое действие удаляет head из person1. Последующие действия позволяют нам увидеть, как это повлияло на наш объект.
Откроется окно Выход (Output), и в нем отобразится:
undefined undefined Justin
Мы видим, что person1.head теперь , как и , находившийся внутри head. Будучи потомком head массив был удален вместе со своим родителем. Однако на прочие свойства объекта person1 это не повлияло – что и продемонстрировало последнее действие , вернув значение person1.name.

Как видите, правильно сконструированный объект реагирует на внешние обстоятельства почти так же, как объекты из реальной жизни. А в соединении с действиями, вызывающими визуальные изменения в фильме, (скоро мы до этого дойдем) объекты способны и на большее.
person1.head = new Object(); trace(person1.head); trace(person1.head.memories[1]);
Первая строка вновь добавляет объект head к person1. Последующие действия позволяют нам увидеть результат. Учтите, что эти три строки скрипта мы поместили после четырех строк, добавленных на первом шаге. Таким образом, при тестировании проекта окно Выход вновь покажет нам результат удаления person1.head ; но на этот раз за ним будет следовать результат возвращения свойства head.
Откроется окно Выход (Output), и в нем отобразится:
undefined undefined Justin [object Object] undefined
Три первых выведенных строки – те же, что мы видели на шаге 2. А две последние показывают, что получилось после возвращения person1 свойства head. В результате person1.head отображено как [object Object], и это значит, что объект head опять существует (и является экземпляром объекта Object), однако person1.head. остался . Причина в том, что массив person1.head. был удален до этого (в результате удаления person1.head ), и "забыт" – выгружен из памяти компьютера (вместе, кстати, со свойством eyes, которое также было частью объекта head ). Когда мы вновь добавили свойство head, компьютер уже не "вспомнил" ни массива , ни свойства eyes. Чтобы восстановить их, пришлось бы создавать эти элементы заново и заново помещать в них данные.
Наша работа с этим файлом завершена. В следующем разделе мы продолжим изучение объектов – и теперь подойдем с другой стороны.
До сего времени мы использовали экземпляр родового объекта Object для демонстрации принципов действия нашего объекта person1. Теперь настало время перейти на следующий уровень механики объектов: создать собственный класс, который мы опишем в этом упражнении как класс Person. Этот класс послужит образцом, "чертежом" для создания новых экземпляров "персон" – вы убедитесь, что такой способ более эффективен для создания person1 и ему подобных.
Примечание В предыдущих упражнениях мы использовали объект Object главным образом ради демонстрации общей концепции объектов. На самом деле объект Object может найти действительно ценное применение в реальных проектах, хотя это – весьма общее решение, которое подойдет не для всякого приложения.
Как и в предыдущих упражнениях, здесь, изучая "теоретическую часть", мы будем частенько пользоваться действием .
Person = function(){
}
Да, это очень похоже на
Примечание В ходе дальнейшего обсуждения темы термины конструктор функции и описание класса будут означать одно и то же.
Обычное Person:
Person(); //выполняется как функция person1 = new Person(); //создается экземпляр класса Person
Примечание Хотя описание класса выглядит, как функция, его следует рассматривать именно как описание класса. Использовать один и тот же блок кода обоими способами не советуем.
Снова повторим, что описание класса мы рассматриваем как чертеж, или шаблон, для создания экземпляров этого класса. Внутри фигурных скобок мы помещаем характеристики, описывающие этот Person не имеет никаких характеристик (свойств), поэтому давайте опишем этот класс, как в нашем предыдущем примере.
Person = function(){
this.name = "Justin";
this.age = 16;
this.legs = 2;
this.head = new Object();
this.head.eyes = 2;
this.head.memories = new Array();
}
Наше описание класса завершено. Далее мы поговорим о том, как его использовать и что означает this в контексте описания класса. Также мы покажем, как создавать экземпляры нашего нового класса.
Совет Возможно, вы обратили внимание, что в описании имя нового класса Person начинается с заглавной буквы. Хотя это не обязательно, такова общая практика. Имена экземпляров класса, напротив, начинаются обычно с маленькой буквы. Соблюдение этого простого правила позволит сделать ваши скрипты более
person1 = new Person();
Этим создается экземпляр объекта Person с именем person1.

Когда откроется окно Выход ( Output ), в нем отобразится следующая информация:
Variable _level0.person1 = [object #2] {
name:"Justin",
age:16,
legs:2,
head:[object #3, class 'Object'] {
eyes:2,
memories:[object #4, class 'Array'] []
}
}
Это – информация об экземпляре person1, который мы создали действием, добавленным на предыдущем шаге. Как определяет наш класс Person, экземпляр person1 имеет имя "Justin", возраст 16, две ноги и так далее.
Важно понимать, что команда person1 = new Person() ; создает экземпляр, свойства которого определены классом Person. Все происходит очень просто: выполняя эту строку кода, Person. Тогда создается временный объект (объект Activation) и отсылается к описанию класса Person, запрограммированному ранее (все это происходит автоматически и внешне никак не проявляется). Объект Activation, поначалу совершенно бесформенный, "проходит" через описание класса, где получает различные свойства класса ( name, age, legs и так далее). В ходе этого процесса временный объект Activation для адресации пользуется термином this, как указано в описании класса. Например, в описании класса встретилась строка:
this.age = 16;
Это значит, что когда объект Activation будет проходить через нее, его свойству age должно быть присвоено значение 16. Процесс создания экземпляра можно представить как движение объекта Activation по сборочной линии конвейера – к нему добавляются по очереди части и он движется дальше. Когда процесс сборки завершен, объект активации получает имя (в данном случае person1 ), и все – он готов к использованию.

Итак, мы описали класс Person и знаем, как создавать его экземпляры, однако все эти экземпляры будут совершенно идентичными. Все дело в том, что так был определен наш класс Person – все его экземпляры будут иметь имя "Justin", возраст 16 и так далее. Хотелось бы, чтоб наш класс Person был способен на что-то большее, чем творить собственных клонов, а потому давайте внесем в описание класса некоторые изменения, чтобы можно было создавать экземпляры с различающимися значениями свойств.
Person = function(name, age){
this.name = name;
this.age = age;
this.legs = 2;
this.head = new Object()
this.head.eyes = 2;
this.head.memories = new Array();
}
Подобно объявлению обычной функции, в нашем описании класса Person теперь указано два аргумента: name и age. Обратите внимание – те же самые значения указаны теперь и в первых двух строках описания класса. Это позволит нам создавать экземпляры класса Person, имеющие уникальные значения свойств.
person1 = new Person ("Justin", 16);
person2 = new Person ("Derek", 29);
Эти два действия создают два экземпляра класса Person. Эти экземпляры имеют разные значения свойств name и age – обновленное описание класса Person позволяет это. Чтобы задать свойства нового экземпляра, мы можем передавать конструктору строки, числа и даже имена объектов.
Теперь хотелось бы упростить создание экземпляров класса Person, чтобы это можно было сделать с любого монтажного стола без указания пути, подобно
_global.Person = function(name, age){
this.name = name;
this.age = age;
this.legs = 2;
this.head = new Object()
this.head.eyes = 2;
this.head.memories = new Array();
}
Обратите внимание на _global в первой строке скрипта: именно это слово позволит создавать экземпляры класса Person с любого монтажного стола, просто используя такой конструктор:
new Person();
Если бы слова _global не было, и описание класса находилось бы, скажем, на основном монтажном столе, то с других монтажных столов пришлось бы использовать следующий синтаксис:
new _root.Person();
Примечание Даже если вы, используя _global, сделаете класс доступным для всех монтажных столов без адресации, отдельные экземпляры все-таки будут находиться на монтажном столе, содержащем скрипт, создавший этот экземпляр. Допустим, вы создаете экземпляр объекта Person с именем myBuddy, причем скрипт, создающий экземпляр, находится в _root.myMovieClip. Тогда абсолютный адрес экземпляра myBuddy будет _root.myMovieClip.myBuddy. Конечно, при желании можно сделать глобальным и экземпляр, использовав такой синтаксис: _global.myBuddy = new Person("Dennis", 27);.
В следующем упражнении мы продолжим работать с этим файлом. Мы теперь умеем создавать экземпляры класса Person, однако этого мало – нужно подумать о его "тонкой настройке" в случае необходимости, а для этого требуется освоить такую мощную штуку, как объект-прототип (звучит жутковато, правда?).
Когда рождается ребенок, мы ожидаем, что у него будет голова, два глаза, два уха, две ноги и так далее. Все это мы ожидаем увидеть у всех детей, поскольку эти признаки жестко зашиты в нашем генетическом коде. То же самое применимо и к объектам, создаваемым Person имена и возраст могут варьироваться, но прочие характеристики являются общими для всех экземпляров. Давайте, рассуждая таким образом, внесем еще изменения в описание нашего класса Person.
_global.Person = function(name, age){
this.name = name;
this.age = age;
}
Person.prototype.legs = 2;
Person.prototype.head = new Object()
Person.prototype.head.eyes = 2;
Person.prototype.head.memories = new Array();
Здесь, как видите, мы вынесли некоторые свойства за пределы формального описания класса и присоединили их к объекту-прототипу класса Person – специальному объекту, связанному с классом. Этот объект (создаваемый автоматически, когда описывается новый класс) содержит свойства и значения, универсальные для данного класса. Можете считать, что этот объект – вроде как прицеп к описанию класса, специальное приложение.

Примечание Все
Объект-прототип класса не будет содержать никаких значений, пока мы их не запрограммируем – поэтому мы и добавили этому объекту три свойства (и присвоили им значения) сразу после описания класса. Эти свойства объекта-прототипа автоматически наследуются всеми экземплярами данного класса, и их значения доступны точно так же. Очень важно уяснить принцип работы объекта-прототипа, поэтому давайте об этом поговорим.
Когда создается новый экземпляр класса Person, описание класса придает ему два свойства – name и age. Вот иллюстрация доступа к одному из этих свойств:
myVariable = person1.age;
Первым делом person1, чтобы определить, имеется ли у этого экземпляра свойство age. Видя, что оно есть (это свойство было присоединено при активации экземпляра – проходе через описание класса), myVariable. Теперь давайте рассмотрим такую строку скрипта:
myVariable = person1.legs;
Вы помните, что описание класса Person теперь не включает свойства legs, тем не менее, myVariable получит значение 2. Дело вот в чем: при таком сценарии legs. Не найдя такого, legs у этого экземпляра, надо посмотреть в объекте Person., может, там есть такое". Тут Person. существует и имеет значение 2, стало быть, и person1.legs равно 2. Это значение myVariable.
Итак,

Вы можете подумать: ведь все эти свойства прекрасно работали, будучи частью описания класса (как в предыдущем упражнении); зачем же морочить голову еще и прототипами? Дело в том, что объект-прототип позволяет распространять изменения на весь класс – то есть на все экземпляры этого класса. Если изменить значение какого-либо свойства в объекте-прототипе класса, то все экземпляры этого класса – вновь создаваемые и существующие – унаследуют это новое значение (за одним исключением, о котором чуть позже). Чтобы вы еще лучше усвоили эту концепцию, давайте немного поупражняемся.
person1 = new Person ("Justin", 16);
person2 = new Person ("Derek", 29);
trace (person1.legs);
trace (person2.legs);
Первые две строки создают два экземпляра объекта Person. Действия выводят значение свойства legs для каждого экземпляра.
Откроется окно Выход (Output), и в нем отобразится следующее:
2 2
Как видите, person1 и person2 имеют одинаковое значение свойства legs. Мы можем сделать изменение для всего класса (то есть изменить значение для всех экземпляров), поменяв значение свойства legs объекта Person.. Давайте так и сделаем.
Откроется окно Выход (Output), и в нем отобразится следующее:
5 5
Как видите, простое изменение одного значения в объекте Person. привело к тому, что и person1, и person2 получили новое значение свойства legs. Если бы у нас было сто экземпляров, все они получили бы это новое значение. Добавление, удаление или изменение свойств объекта-прототипа класса немедленно отражаются на всех экземплярах этого класса (единственное исключение, о котором мы уже упоминали, мы обсудим в следующем разделе).
Итак, экземпляры объекта могут иметь как уникальные свойства, так и универсальные (одинаковые для всех экземпляров данного класса). Например, мы все имеем разные имена и возраст (свойства, заложенные в описании класса), но также мы имеем некоторые общие признаки – две ноги, голова, и на ней два глаза (свойства, устанавливаемые объектом-прототипом).
Мы продолжим работу с этим файлом в следующем упражнении.
Как упоминалось в предыдущем разделе, при попытке обращения к значению свойства – например, присвоении myVariable значения person1.legs (myVariable = person1.legs;) – person1, созданный нами в предыдущем упражнении, имеет следующие свойства:
name = "Justin" // уровень экземпляра age = 16 // уровень экземпляра legs = 2 // уровень прототипа head = new Object() // уровень прототипа head.eyes = 2 // уровень прототипа head.memories = new Array() // уровень прототипа
Почему важно различать свойства уровней экземпляра и прототипа? В ситуации, когда свойства уровня экземпляра и уровня прототипа имеют одно и то же имя, значение свойства уровня экземпляра перекрывает значение уровня прототипа (хотя это относится только к данному экземпляру).
Допустим, экземпляр person1 получает свойство legs от объекта Person., поскольку при создании экземпляра это свойство не было присвоено. Однако если мы присвоим значение этому свойству, обратившись непосредственно к экземпляру – например, person1.legs = 1 ; – мы создадим у person1 свойство legs уровня экземпляра, и его значение перекроет значение, установленное прототипом. У всех прочих экземпляров останется две ноги – за исключением person1, который теперь имеет только одну. Значение уровня прототипа попрежнему существует и для этого экземпляра, только теперь оно невидимо, перекрыто свойством уровня экземпляра с таким же именем.

Мы можем удалить свойство legs уровня экземпляра – вот таким образом:
delete person1.legs;
В результате person1 вновь станет отражать свойство legs (и его значение), установленное объектом-прототипом класса Person.
Еще один важный аспект, касающийся свойств уровня экземпляра – то, что благодаря им экземпляры могут иметь свои уникальные свойства вдобавок к полученным из описания класса и унаследованным от прототипа. Чтобы добавить такое свойство, нужно просто обратиться непосредственно к экземпляру. Например:
person2.favoriteFood = "Pizza";
Теперь person2 станет единственным экземпляром, имеющим свойство favoriteFood. Конечно, мы можем запросто добавить такое свойство и прочим экземплярам (присвоив его напрямую либо добавив в объект-прототип), но пока мы этого не сделаем, свойство favoriteFood будет только у экземпляра person2.
Примечание Если добавить свойство в описание класса, то это свойство будут получать лишь вновь создаваемые экземпляры, начиная с этого момента, но не те, что уже существуют. Причина в том, что конструктор функции (описание класса) присоединяет свойства к экземплярам лишь в процессе их создания.
Свойства уровня экземпляра позволяют индивидуализировать экземпляры, не принося в жертву преимущества
Наш класс Person приобретает все большую функциональность: мы можем создавать его экземпляры, указывая для них имена и возраст, и еще они могут наследовать свойства объекта-прототипа. Но, как в реальной жизни, могут существовать еще и классы внутри нашего класса Person: доктора, юристы, Person (имя, возраст, ноги, голова), но имеют и свои, уникальные. Если, например, вы создаете класс Доктор, он должен обладать такими особыми характеристиками, как специальность, звание (" Dr."), и так далее, и в то же время – всеми признаками класса Person.
_global.Doctor = function(almaMater){
this.almaMater = almaMater;
}
Doctor.prototype.title = "Dr.";
Здесь мы создаем конструктор функции, описывающий новый Doctor. Каждый новый экземпляр этого almaMater, значение которого передается конструктору функции при создании экземпляра. Также мы придали классу Doctor прототип со свойством title, имеющим значение "Dr.". Поскольку это звание носят все доктора, мы можем поместить его в объект-прототип класса Doctor.

Давайте создадим экземпляр нашего нового класса.
frankenstein = new Doctor("Transylvania");
trace(frankenstein.almaMater);
trace(frankenstein.title);
trace(frankenstein.legs);
Первое действие создает новый экземпляр класса Doctor с именем frankenstein. Последующие действия
Откроется окно Выход (Output), и в нем отобразится следующее:
Transylvania Dr. undefined
Как видите, со свойством almaMater все в порядке (это свойство уровня экземпляра, и оно было добавлено при создании экземпляра), и со свойством title тоже (благодаря объекту Doctor. ), а вот свойство legs не существует.
Нашей целью было сделать класс Doctor подклассом класса Person, чтобы все экземпляры класса Doctor наследовали такие свойства, как legs, head и так далее. Только это не произойдет само собою – где-либо в нашем скрипте мы должны сообщить Doctor есть Person.
Doctor.prototype .title = "Dr." :Doctor.prototype = new Person();
Эта строка скрипта создает связь между классами Doctor и Person. Она указывает, что объект-прототип класса Doctor должен наследовать объекту-прототипу Person.
В результате все экземпляры класса Doctor будут отражать свойства не только Doctor., но также и Person.. Обратное неверно; экземпляры класса Person будут отражать только свойства Person.. Теперь Doctor – Person, а Person – Doctor.
Существует причина, по которой мы поместили нашу последнюю строку перед строкой устанавливающей свойство title объекта Doctor.. При выполнении добавленной нами строки объект Doctor., по существу, полностью очищается, все свойства, которые в нем могли быть, заменяются свойствами объекта Person.. Если бы мы добавили в объект Doctor. свойство title до этого, оно было бы утрачено. Вместо этого в нашем скрипте мы добавляем свойство title после того, как Doctor. наследует объекту Person, и проблема решена.
В конце нашего скрипта остались три действия , которые прежде (на шаге 3) вернули такие результаты:
trace(frankenstein.almaMater);// вернуло "Transylvania" trace(frankenstein.title); );// вернуло "Dr." trace(frankenstein.legs);// вернуло undefined
Давайте проверим результаты теперь.
Откроется окно Выход (Output), и в нем отобразится следующее:
Transylvania Dr. 2
Как видите, frankenstein имеет теперь две ноги (тогда как прежде это свойство было не определено – ). Это результат того, что экземпляры Doctor теперь наследуют, как мы выяснили на предыдущем шаге, свойства не только Doctor., но и Person.. По той же причине frankenstein имеет теперь голову – head, а в голове – , и так далее. Если теперь мы изменим любое свойство Person., эти изменения отразятся не только на экземплярах класса Person, но также и на экземплярах класса Doctor, ведь он – Person. Дело, по существу, обстоит так. Когда legs экземпляра frankenstein (на уровне экземпляра), он такого свойства не находит. Тогда он заглядывает в объект Doctor. (поскольку frankenstein есть экземпляр класса Doctor
). Такого свойства нет и там. Наконец Person. (поскольку Doctor есть Person ) – и здесь находит-таки свойство legs.
Теперь вы, возможно, заинтересовались такими свойствами класса Person, как name и age. Эти свойства не входят в объект Person., так будут ли экземпляры класса Doctor наследовать их? Нет, не будут. Чтобы исправить это, нам нужно внести изменения в описание класса Doctor.

Doctor следующим образом:_global.Doctor = function(name, age, almaMater){
this.base = new Person(name, age);
this.name = this.base.name;
this.age = this.base.age;
delete this.base;
this.almaMater = almaMater;
}
Первое, что вы замечаете в обновленном описании класса Doctor – то, что появились два новых аргумента (вдобавок к almaMater, который был и прежде): name и age. Все три значения используются в последующих строках, обеспечивающих создание экземпляров класса Doctor.
В первой строке используются значения name и age, переданные конструктору функции, для создания нового экземпляра класса Person. Да-да, конструктор функции Doctor создает экземпляр класса Person. Как видите, этот экземпляр имеет имя и путь this.base. Видя такую строку, this.base получает свойства name и age (как все экземпляры класса Person ). Следующие две строки конструктора функции задают значения свойств name и age для будущих экземпляров класса Doctor ( this.name и this.age ), причем значения берутся у this.base (экземпляр класса Person, созданный в первой строке). Четвертая строка конструктора функции удаляет экземпляр this.base – он был нужен лишь временно. Пятая строка задает свойство almaMater для будущих экземпляров
(это уже было на шаге 1).
Примечание Это показывает, сколь динамичным может быть при необходимости конструктор функции: функция может создать временный объект (как было показано), выполнить математические вычисления, другие действия, и много чего еще.
Вы, вероятно, думаете, мол, к чему такие сложности, разве нельзя было выполнить описание класса вот так:
_global.Doctor = function(name, age, almaMater){
this.name = name;
this.age = age;
this.almaMater = almaMater;
}
Верно, так было бы проще, и в большинстве случаев подошло бы, но представьте себе, что в описании класса Person с переданными ему значениями name и age производятся какие-либо действия – скажем, все буквы name делаются заглавными, или что-то в этом роде? Тогда в последнем варианте скрипта экземпляры класса Doctor оказались бы лишены этого, тогда как в первоначальном такого не случится.
frankenstein = new Doctor("Frankenstein", 85, "Transylvania");
Необходимость такого изменения вызвана тем, что мы добавили в конструктор функции Doctor параметры age и name. В результате экземпляр frankenstein при создании получит свойства name со значением "Frankenstein", age со значением 85 и almaMater со значением "Transylvania".
trace(frankenstein.name); trace(frankenstein.age);
Эти действия позволят нам увидеть результаты сделанных изменений. Всего в конце скрипта у нас получилось пять действий (три должны остаться с шага 2).
Откроется окно Выход (Output), и в нем отобразится следующее:
Transylvania Dr. 2 Frankenstein 85
Экземпляр frankenstein полностью готов!
Теперь пришло время представить для рассмотрения свойство _proto_. Каждый объект-прототип класса и каждый экземпляр класса имеют свойство _proto_. Его значение показывает, где следует искать свойства, не обнаруженные у самого экземпляра. Как мы знаем, для экземпляра frankenstein в таком случае поиск будет продолжаться в объекте Doctor.. Таким образом, frankenstein._proto_ имеет значение Doctor. (экземпляр наследует этому объекту-прототипу). Поскольку класс Doctor наследует классу Person, Doctor. равно Person. (один прототип класса наследует другому объекту-прототипу класса). Это свойство _proto_ создается и получает значение автоматически при создании экземпляра или Lawyer и хотим связать frankenstein
с этим классом (вместо класса Doctor ). Тогда мы просто используем такую строку скрипта:
frankenstein._proto_ = Lawyer.prototype;
После этого frankenstein можно считать экземпляром класса Lawyer, наследующим характеристики прототипа этого класса. При этом его "докторские" характеристики (унаследованные от объекта Doctor. ) исчезнут начисто!
Осталось рассказать еще о двух важных аспектах Person и Doctor ), то обнаружим следующее:
Person имеет name: это состав.Person имеет head: это состав.Person имеет legs: это состав.Doctor есть Person: это наследование.Doctor имеет title: это состав.Эта концепция имеет место и в реальном мире. Вот, например:
Рассуждая таким образом, вам будет легко проектировать новые
Наследование также имеет некоторые свои аспекты, но это уже выходит за пределы тематики данной книги. Теперь, когда вы понимаете, как создаются любые
В следующем упражнении мы продолжим работу с этим файлом.
До сих пор объекты, которые мы создавали, имели свойства, значения которых мы могли использовать для вычислений и тому подобного, однако они не могли сами выполнять каких-либо задач. Сейчас вы поймете, что это означает – просто вспомните, что умеют делать
play (), остановить – stop(), загрузить фильм – loadMovie(), получить URL – getURL() и так далее.loadSound(), установить громкость – setVolume(), установить баланс – setPan() и тому подобное.А наши классы Person и Doctor ничего делать не могут, кроме как просто "присутствовать". В этом разделе мы постараемся изменить такое положение дел, создав для наших классов методы – это даст возможность экземплярам этих классов выполнять полезные задачи.
Прежде всего следует вам сказать, что метод – это не что иное, как функция, связанная с классом и помещенная в объект-прототип этого класса. Давайте начнем упражнение, и вы увидите, как это работает.
Person.prototype.talk = function(){
trace("Привет, меня зовут "+ this.name +". Мне "+ this.age +" лет.");
}
Примечание Можно создать метод другим способом:
//Сначала описываем функцию
function futureMethod(){
trace("Привет, меня зовут "+ this.name +". Мне "+ this.age +" лет.");
}
//Назначаем эту функцию методом talk класса Person
Person.prototype.talk = futureMethod;

Вот и создан новый метод для Person. Функция сделалась методом класса Person, поскольку мы поместили ее в объект-прототип класса Person.
Этот наш метод, который называется talk(), может быть использован любым экземпляром данного класса. У нас имеются экземпляры person1 и person2. Любой из этих экземпляров может использовать наш новый метод, вот таким образом:
person2.talk();
Этот метод выполняет лишь одну простую задачу: выводит сообщение в окне Выход при тестировании фильма. Как вы видели, в описании метода, в действии , использованы элементы this.name и this.age. Поскольку функция является методом, this есть ссылка на экземпляр, который вызвал этот метод. Таким образом, если этот метод вызывает person2, эти элементы будут означать person2.name и person2.age.
Примечание Если термин this используется в обычной функции, то он означает ссылку на монтажный стол, на котором находится функция.
person2.talk();
Это приведет к тому, что person2 вызовет метод talk().
Откроется окно Выход (Output), и в нем отобразится следующее:
Привет, меня зовут Derek. Мне 29 лет.
Итак, наш метод talk() дал возможность экземплярам нашего класса Person хоть что-то делать. Давайте немного расширим возможности этого метода.
Person.prototype.talk = function(tone){
var message = "Привет, меня зовут "+ this.name +". Мне "+ this.age +" лет.";
if (tone == "крик"){
trace(message.toUpperCase());
}else{
trace(message);
}
}
Как видите, теперь метод принимает параметр . Метод будет действовать одним из двух способов в зависимости от значения параметра. Давайте рассмотрим подробнее обновленное описание метода.
Первым делом создается message, которая получает значение, полученное из выражения. Выражение то же самое, которое мы прежде использовали в действии .
Примечание Как упоминалось в предыдущем уроке (о функциях), message локальной, она была бы доступна любому скрипту в любое время. Будучи локальной, она будет удалена, как только завершится выполнение метода.
После того, как переменная message получила значение, в действие вступает оператор if. Он проверяет значение : если оно равно "крик", то значение message преобразовывается – все буквы делаются заглавными – а затем выводится в окне Выход. Если имеет любое другое значение, message будет выведено обычным образом. Использование метода toUpperCase() объекта String демонстрирует, что метод одного класса может использовать методы других классов. Методы могут содержать действия, другие методы и так далее – практически все, что предоставляет
person2.talk("крик");
В результате person2 вызовет метод talk(), передав ему в качестве параметра значение "крик". Теперь обе последние строки вызывают метод talk(), но разными способами (в одном случае передается значение "крик"); окно Выход покажет нам оба результата.
Откроется окно Выход (Output), и в нем отобразится следующее:
Привет, меня зовут Derek. Мне 29 лет. ПРИВЕТ, МЕНЯ ЗОВУТ DEREK. МНЕ 29 ЛЕТ.
Теперь давайте рассмотрим еще один интересный аспект нашего метода talk().
frankenstein.talk();
frankenstein.talk("крик");
Метод talk() принадлежит классу Person, однако, поскольку класс Doctor есть Person, экземпляры класса Doctor тоже имеют доступ к этому методу – как вы видите на примере экземпляра frankenstein.
Класс Doctor тоже может иметь свои, уникальные, методы – достаточно просто поместить нужные функции в объект Doctor., как мы сделали с объектом Person. на шаге 1.

Примечание Если создать другой метод talk() – в объекте Doctor., то экземпляр frankenstein будет использовать именно этот метод, а не тот, что находится в Person.. Ведь frankenstein – экземпляр класса Doctor, и поиск его методов происходит в первую очередь в объекте Doctor..
Откроется окно Выход (Output), и в нем отобразится следующее:
Привет, меня зовут Frankenstein. Мне 85 лет. ПРИВЕТ, МЕНЯ ЗОВУТ FRANKENSTEIN. МНЕ 85 ЛЕТ.
Наш метод talk() выполняет простейшую задачу, вообще же методы могут быть весьма сложными.
Полученные знания о механике объектов пригодятся вам в следующих разделах, когда мы будем изучать способы расширения функциональности
Итак, вы знаете, что объектам (в том числе фильмам-символам) можно придать свойства (переменные). В типичных watch() для отслеживания различных свойств (переменных, как условия в нашем примере) объекта; если обнаружено изменение значения свойства, то вызывается некая функция, и ваш проект реагирует на изменение. Синтаксис применения этого метода таков:
имяОбъекта.watch("имяСвойства", имяФункции);
Предположим, weather (погода) – имя фильма-символа на основном монтажном столе, (условия) – . Тогда мы запишем так:
_root.weather.watch("condition", watchCondition);
Примечание Объект может инициировать отслеживание внутри себя, используя синтаксис this.watch("имяСвойства", имяФункции);.
Функция watchCondition() могла бы быть описана, например, так:
function watchCondition(id, oldval, newval){
message.text = id + " было " + oldval + ", а теперь " + newval;
if (newval == "rainy"){
sky.gotoAndStop("dark");
lights.gotoAndStop("on");
wipers.gotoAndPlay("on");
}else if (newval == "sunny"){
sky.gotoAndStop("bright");
lights.gotoAndStop("off");
wipers.gotoAndStop("off");
}
}
Обратите внимание на параметры id, oldval и newval, передаваемые функции. Эти параметры будут автоматически передаваться функции при ее вызове в результате срабатывания метода watch(). Параметр id представляет собой имя (в виде строкового значения) отслеживаемого свойства; oldval – старое значение этого свойства (до изменения), а newval – новое значение, которое привело к срабатыванию метода watch(). В приведенном примере вы видите, как эти параметры используются внутри функции.

Примечание Хотя в примере этого не показано, если бы в функции встречалась адресная ссылка this, она означала ссылку на объект, свойство которого отслеживается.
Примечание Необходимо описывать функцию, вызываемую методом watch(), до того, как будет инициировано отслеживание. Ведь в качестве параметра метода watch() вы должны указать функцию, которую следует вызвать при изменении свойства, а указать функцию, которая еще не существует, нельзя.
Если необходимость в отслеживании свойства отпала, вы можете отменить его, вызвав метод unwatch():
weather.unwatch("condition");
После выполнения данной строки скрипта при изменении свойства функция watchCondition() более не будет вызываться.
Несколько важных моментов, касающихся применения метода watch():
watch() работает с большинством объектов _x, _y, _alpha и тому подобные.В следующем упражнении мы создадим сцену, в которой изменение отслеживаемого свойства будет влиять на различные элементы фильма.
Большинство элементов этой сцены мы для вас уже создали и настроили, так что вы можете сосредоточиться на message. В слой Buttons помещены все используемые в нашем проекте кнопки. Четыре из них, которые мы будем использовать в этом упражнении, расположены справа от экземпляра bigHead. Имена кнопок – mouthButton, noseButton, eyesButton и glassesButton. Две другие кнопки, те, что в левом верхнем углу сцены, мы используем в следующем упражнении. Слой Actions содержит скрипты (к которым мы многое добавим по ходу упражнения).

Главное, о чем следует вас предупредить – это то, что монтажный стол этого фильма-символа (сам по себе простенький) содержит экземпляры четырех других фильмов-символов: glassesClip, eyesClip, noseClip и mouthClip. Что они изображают, можно догадаться по названиям – очки, глаза, нос и рот. Мы будем делать эти экземпляры видимыми и невидимыми, отслеживая изменение значений свойств. В нашем фильме два экземпляра фильма-символа Head, и мы запрограммируем их так, чтобы они реагировали на события противоположным образом. Например, если экземпляр noseClip в экземпляре bigHead становится видимым, то noseClip в экземпляре smallHead делается невидимым.
onClipEvent(load){
glassesClip._visible = false;
eyesClip._visible = false;
noseClip._visible = false;
mouthClip._visible = false;
glasses = false;
eyes = false;
nose = false;
mouth = false;
}
Здесь использовано событие load, то есть скрипт будет выполнен, как только экземпляр bigHead появится на сцене – а это произойдет сразу после начала воспроизведения фильма. Кстати, большинство скриптов для этого упражнения мы будем помещать в обработчики события load. А пока давайте рассмотрим эти первые несколько строк.
Четыре первые строки делают невидимыми экземпляры, о которых мы говорили на предыдущем шаге. Таким образом, сразу после загрузки bigHead лишится лица, а smallHead останется без изменений, (то есть, с лицом) – ведь эти действия затрагивают только экземпляры, находящиеся в bigHead.
Четыре следующие строки присваивают значения переменным. Имена переменных (и значения) совпадают с текущим состоянием экземпляров фильмов-символов, о которых только что шла речь. Эти четыре переменные (свойства) мы будем отслеживать на предмет изменений. Сейчас мы присвоили им начальные значения.
Теперь нам нужно описать функции, которые будут вызываться при изменении одного из этих свойств.
function watchGlasses(id, oldval, newval){
_root.message.text = "I now have " + id + ".";
glassesClip._visible = newval;
_root.smallHead.glassesClip._visible = oldval;
_root.glassesButton.enabled = false;
}

Вообще-то мы собираемся установить наблюдение за переменной , а эта функция будет выполняться при изменении значения. Обратите внимание на три параметра, передаваемых функции – id, oldval, newval. Значения всех трех параметров используются в функции. Первая строка функции, используя значение id, будет выводить сообщение в текстовом поле message. Поскольку эту функцию будет вызывать "наблюдатель" за значением , id в данном случае будет иметь строковое значение "newval. На предыдущем шаге мы дали очкам начальное значение false ; это значит, что функция будет вызвана, когда это значение сменится на true, а значит, и newval будет true – это ведь не что иное, как новое значение свойства или переменной. Итак, во второй строке glassesClip._visible становится true, и glassesClip из bigHead отображается на экране. В следующей строке нашел применение параметр oldval – в соответствии с ним устанавливается видимость glassesClip из экземпляра smallHead. А поскольку oldval имеет значение false (значение glassesClip из smallHead становится невидимым. Последнее действие отключает кнопку glassesButton. Функции этой кнопки мы пока не запрограммировали, но заранее скажем, что именно она будет изменять значение , что и приведет к выполнению данной функции. Как видите, изменив это значение, кнопка отключается, то есть, использовать ее можно будет лишь один раз.
function watchEyes(id, oldval, newval){
_root.message.text = "I now have " + id + ".";
eyesClip._visible = newval;
_root.smallHead.eyesClip._visible = oldval;
_root.eyesButton.enabled = false;
}
function watchNose(id, oldval, newval){
_root.message.text = "I now have " + id + ".";
noseClip._visible = newval;
_root.smallHead.noseClip._visible = oldval;
_root.noseButton.enabled = false;
}
function watchMouth(id, oldval, newval){
_root.message.text = "I now have " + id + ".";
mouthClip._visible = newval;
_root.smallHead.mouthClip._visible = oldval;
_root.mouthButton.enabled = false;
}
Эти три функции будут обрабатывать изменение переменных eyes, nose и mouth, соответственно. На шаге мы задали для всех этих переменных начальное значение false ; значит, соответствующие функции будут выполняться при изменении значения на true. Работают эти функции точно так же, как та, которую мы создали на предыдущем шаге. Различия заключаются лишь в именах самих функций, в именах экземпляров, которые становятся видимыми (в bigHead ) и невидимыми ( smallHead ), и именах отключаемых кнопок.
Итак, функции созданы, пора заняться и "наблюдателями".
Примечание Помните, что функции, используемые методом watch(), должны быть описаны до того, как будет установлен сам "наблюдатель".
this.watch("glasses", watchGlasses);
this.watch("eyes", watchEyes);
this.watch("nose", watchNose);
this.watch("mouth", watchMouth);
Эти четыре строки содержат указания . При изменении значения этого свойства выполняется функция watchGlasses(). Обратите внимание на использование здесь термина this. Первая строка, в сущности, гласит: "Отслеживать изменение значения свойства в "этом" ( this ) объекте". Если бы переменная находилась в другом экземпляре, мы могли бы попросту использовать адресацию – указать путь к этому экземпляру (и/или функции), к примеру, вот так:
_root.myMovieClip.watch("glasses", _root.myFunction);
Остальные три строки скрипта устанавливают наблюдение за свойствами eyes, nose и mouth, и при их изменении выполняют соответствующие функции.
Нам осталось лишь запрограммировать четыре кнопки, которыми мы и будем изменять значения четырех свойств.
on(release){
_root.bigHead.mouth = true;
}

Как только эта кнопка будет нажата и отпущена, значение свойства mouth экземпляра bigHead изменится с false на true, и на это отреагирует "наблюдатель", установленный нами на предыдущем шаге.
К кнопке noseButton:
on(release){
_root.bigHead.nose = true;
}
К кнопке eyesButton:
on(release){
_root.bigHead.eyes = true;
}
К кнопке glassesButton:
on(release){
_root.bigHead.glasses = true;
}
Все то же самое, что и с предыдущей кнопкой. Различаются только свойства, значения которых изменяются при нажатии каждой из кнопок.
Итак, изначально bigHead у нас совершенно "обезличен". Нажатие той или иной кнопки справа изменит значение mouth, nose, eyes либо , что приведет к выполнению одной из наших функций. В сцене произойдут изменения – как мы и запрограммировали.
На этом упражнение закончено. Мы увидели как используется отслеживание свойств – единственное изменение значения может запустить нужную функцию. Конечно, в этом упражнении можно было обойтись без всякого отслеживания – попросту вызывать нужные функции кнопками. Вообще же технология отслеживания свойств может быть очень и очень полезна – особенно в ситуации, когда изменение единственного свойства объекта имеет решающее значение для общей функциональности проекта. В таких случаях бывает проще сразу установить наблюдение за нужным свойством, чем потом то и дело проверять его значение, чтобы вызвать функцию. Да и вообще со свойствами как-то удобнее работать.
Встроенные во
Усовершенствовать существующие методы можно двумя способами – в зависимости от типа объекта. Для некоторых объектов нельзя создать экземпляры – они единственны и универсальны для каждого проекта: это объекты и Math. К примеру, вы не можете создать экземпляр объекта – мышь у компьютера только одна, и на экране только один указатель мыши; вы можете лишь использовать методы объекта и т.п.). Итак, если экземпляр объекта создать нельзя, то для усовершенствования метода этого объекта используется такой способ:
MathPlus = new Object();
MathPlus._proto_ = Math;
MathPlus.oldMax = Math.max;
MathPlus.max = function(x, y, z){
firstComparison = MathPlus.oldMax(x, y);
secondComparison = MathPlus.oldMax(firstComparison, z);
return secondComparison;
}
Object.prototype.Math = MathPlus;
trace(Math.max(23, 56, 4));
Данный скрипт усовершенствует метод max() объекта Math, который в нормальном своем виде находит большее из двух чисел. Например, в этом примере myVariable получит значение 456:
myVariable = Math.max(456, 23);
Приведенный выше скрипт изменяет этот метод так, чтобы он находил большее из трех чисел. Вот как это делается: в первой строке создается родовой объект с именем MathPlus. В следующей строке используется свойство _proto_, чтобы экземпляр этого нового объекта унаследовал все функциональные возможности объекта Math.
Примечание Здесь мы не можем обеспечить наследование, используя выражение MathPlus = new Math() ; (как мы делали прежде с экземплярами Doctor и Person ), поскольку экземпляр объекта Math создать нельзя – выражение new Math() не имеет смысла. Поэтому, чтобы объект MathPlus унаследовал возможности объекта Math, мы должны использовать свойство _proto_, как и показано в скрипте – результат, в принципе, тот же самый, только достигается иным путем (об этом свойстве мы рассказывали в этом уроке, в разделе "Создание
Теперь MathPlus "умеет" делать все, на что способен объект Math. Например, Math. ; и MathPlus. ; дадут один и тот же результат. Почему мы решили пойти таким путем? Дело в том, что напрямую манипулировать методами объекта Math (как и других объектов, не позволяющих создавать экземпляры) нельзя. Поэтому мы создаем объект-посредник ( MathPlus ), который наследует возможности объекта Math, изменяем функции этого посредника, а затем (такое вот извращение) велим MathPlus ) объектом Math.
Итак, в двух следующих строках скрипта описываются два новых метода объекта MathPlus: oldMax, который копирует оригинальный метод Math.max(), и max, который станет новым, усовершенствованным методом max(). Метод oldMax создать необходимо, поскольку функции оригинального метода max() все же понадобятся новому методу max(). Однако мы должны дать оригинальному методу новое имя, так как имя max будет теперь принадлежать новому, измененному методу. Функция, описывающая новый метод max(), принимает три параметра ( x, y, z ). Значения этих трех MathPlus методом Math прототипа класса Object (это значит, что объект MathPlus заместит прежний объект Math ).

Можем заодно усовершенствовать метод объекта Math, чтобы он генерировал случайное число из заданного диапазона. Это можно сделать с помощью таких строк (предполагается, что MathPlus уже создан и "получил наследство", как показано в первых двух строках предыдущего примера):
MathPlus.oldRand = Math.random;
MathPlus.random = function(min, max){
return MathPlus.floor(MathPlus.oldRand() * ((max - min) + 1)) + min;
}
Как видите, синтаксически это похоже на предыдущий пример. С помощью подобных синтаксических структур можно усовершенствовать и методы объектов .
Способ изменения методов таких классов, которые позволяют создавать экземпляры (например, и т.п.), отличается – но не намного. В этом упражнении мы усовершенствуем метод duplicateMovieClip(). Используя существующий метод, мы имеем возможность указать, какой фильм-символ следует дублировать, какое имя дать дубликату и на какую глубину следует поместить дубликат (подробнее об этом методе см. в Уроке 14 – Динамическое управление фильмами-символами). А мы усовершенствуем этот метод, добавив возможность перемещать оригинальный фильм-символ (тот, который дублируется) в указанные xy координаты по окончании процесса дублирования.
Мы уже работали с этим проектом в предыдущем упражнении, так что его элементы должны быть вам знакомы. В этом упражнении мы присоединим скрипты к кадру 1, а также к кнопкам (в виде сдвоенных квадратов), расположенным в левом верхнем углу сцены.
MovieClip.prototype.oldDuplicateMovieClip =
MovieClip.prototype.duplicateMovieClip;
MovieClip.prototype.duplicateMovieClip = function(name, depth, moveX, moveY){
this.oldDuplicateMovieClip(name, depth);
this._x = moveX;
this._y = moveY;
}
Мы поместили этот скрипт в кадр 1, значит, как только начнется воспроизведение фильма, метод duplicateMovieClip() будет изменен, после чего, в новом виде, станет доступен всем экземплярам фильмов-символов.
Методами объекта Math мы напрямую манипулировать не могли (пришлось использовать объект-посредник), а вот методами класса – вполне можем, поскольку эти методы находятся в объекте , который нам ничего не стоит изменить.
Первая строка скрипта копирует оригинальный метод duplicateMovieClip() в oldDuplicateMovieClip(). Это мы делаем для того, чтобы сохранить его функциональность, она нам пригодится, когда мы будем описывать работу усовершенствованного метода. Со следующей строки начинается функция, описывающая новый, измененный метод duplicateMovieClip(). Этот метод будет принимать два дополнительных параметра: moveX и moveY (сейчас мы поясним, как они будут использованы). В первой строке функции используется метод oldDuplicateMovieClip() – он выполняет то, что раньше делал оригинальный метод duplicateMovieClip(), то есть дублирует фильм-символ, дает ему имя и назначает глубину. Примененный здесь термин this ссылается на оригинальный экземпляр фильма-символа (тот, который дублируется). Следующие две строки, используя значения параметров moveX и moveY, перемещают оригинальный экземпляр в соответствующую координатную точку. Все – наш новый метод duplicateMovieClip() готов к использованию экземпляром любого фильма-символа!
on(release){
smallHead.duplicateMovieClip("myHead1", 10, 200, 150);
}

Когда эта кнопка будет нажата и отпущена, произойдет дублирование экземпляра smallHead. Дубликат получит имя myHead1 и глубину 10. Два других параметра – это значения, соответственно, moveX и moveY, они используются для перемещения оригинального экземпляра после дублирования. Таким образом, smallHead будет перемещен в точку с координатами 200 по x и 150 по y.
on(release){
bigHead.duplicateMovieClip("myHead2", 10, 100, 200);
}
Здесь (таким же образом) будет продублирован экземпляр bigHead.
При нажатии на каждую из запрограммированных нами кнопок соответствующий экземпляр будет дублирован и перемещен в указанную точку координат.
В нашем примере изменение существующего метода был простым, однако такие усовершенствования могут быть весьма сложными – процесс может включать очень много действий TextField, и т.д.).
Это упражнение завершено, но в следующем мы продолжим работу над данным проектом.
В предыдущем упражнении вы узнали, как можно усовершенствовать существующий метод
Как и при изменении существующих, тут подход тоже зависит от того, создается ли новый метод для встроенного объекта ( ) или и т.п.).
Чтобы создать новый метод для встроенного объекта (мы опять возьмем для примера объект Math) следует использовать такую синтаксическую конструкцию:
Math.totalWithTax = function(total, taxPercent){
convertedPercent = taxPercent / 100;
totalTax = total * convertedPercent;
return total + totalTax;
}
trace(Math.totalWithTax(24.00, 6)); // Выводит результат: 25.44
Как видите, чтобы добавить метод (в данном случае – totalWithTax() ) к объекту Math, мы попросту присоединили к нему функцию. Теперь этот метод будет доступен в любом месте фильма. Таким же образом присоединяются новые методы и к другим
В следующем упражнении мы покажем, как добавить новый метод к классу , но описанный способ подойдет и для других
С этим проектом мы работали, выполняя два предыдущих упражнения, так что с его элементами вы знакомы. В этом упражнении мы добавим скрипт в кадр 1, а также к кнопкам в виде двусторонних стрелок, что в левой верхней части сцены.
MovieClip.prototype.flip = function(mode){
if (mode.toLowerCase() == "h"){
this._xscale = -this._xscale;
}else if (mode.toLowerCase() == "v"){
this._yscale = -this._yscale;
}
}

Мы поместили этот скрипт в кадр 1, поэтому он будет выполнен сразу после начала воспроизведения фильма, и после этого метод , добавленный нами в объект MoviClip.mode. Его значение может быть либо "h", либо "v" –указание на поворот по горизонтали или вертикали.
В зависимости от значения mode функция решает, как действовать – помогает ей в этом оператор if. В первой секции этого оператора значение mode преобразуется в нижний регистр (строчную букву), после чего сравнивается со значением "h". Если преобразованное значение mode равно "h", значит, нужно переворачивать по горизонтали. Делается это путем изменения знака текущего значения xscale (масштаб по x) данного экземпляра фильма-символа ( this ). То есть, если это значение равно 75, то будет –75; если оно равно –34, то будет 34. Обращение значения выполняется при помощи оператора "минус" (-). При таком использовании этот оператор превращает положительное число в отрицательное и наоборот. Таким вот образом масштабирование фильма-символа приспособлено для переворачивания.
Секция else вступает в дело, если mode имеет значение "v". В этом случае осуществляется вертикальный переворот – меняется на знак свойства yscale.
Примечание Преобразование значения mode в символ нижнего регистра мы предусмотрели на случай нечаянного ввода "H" или "V" вместо "h" и "v" – ведь "H" не равно "h". Такое преобразование помогает снизить вероятность ошибки при использовании нового метода.
on(release){
smallHead.flip("v");
}
При нажатии этой кнопки экземпляр smallHead будет переворачиваться по вертикали.
on(release){
smallHead.flip("h");
}
Это будет переворачивать экземпляр smallHead по горизонтали.
При нажатии на какую-либо из этих кнопок тот или иной экземпляр фильма-символа будет переворачиваться по горизонтали или вертикали.

Это был простой пример того, как можно создать свой метод для TextField, и т.п.).
Это упражнение завершено, а в следующем мы продолжим работу со слегка модифицированной версией этого проекта.
Совет Отличный сетевой ресурс, содержащий новые и усовершенствованные методы
Совет Вы можете создать собственную библиотеку дополнительных методов (а также и классов), написав нужный код в стандартном текстовом редакторе и сохранив файл с расширением .as – что-нибудь вроде CustomScript.as – файлы с таким расширением #include, указав имя файла, который следует включить в программу (например, #include "CustomScript.as" ). Это действие автоматически импортирует и вставляет код из .as файла в то место программы, где находится действие #include. Таких .as файлов, содержащих код для повторного использования, вы можете создать сколько угодно.
Одна из наиболее ценных новинок clipOne и clipTwo.
Примечание Обратите внимание, это важно: далее в тексте, говоря о фильме-символе, мы будем иметь в виду образец, хранящийся в библиотеке. Экземпляр фильма-символа или просто экземпляр, является копией этого образца.
Итак, давайте создадим новый класс:
_global.CustomClass = function(){
}
Мы создали новый класс под именем CustomClass. Далее мы укажем, что этот класс должен наследовать возможности класса – для этого сразу после описания класса мы поместим такую строку:
CustomClass.prototype = new MovieClip();
Теперь придадим нашему классу дополнительные возможности, добавив их в объект-прототип:
CustomClass.prototype.onRollOver = function(){
this._alpha = 50;
}
CustomClass.prototype.onRollOut = function(){
this._alpha = 100;
}
CustomClass.prototype.onEnterFrame = function(){
this._rotation += 10;
}
Наш класс получил, посредством трех методов – обработчиков событий, три функции интерактивного взаимодействия. Теперь нужно, чтобы экземпляры фильма-символа clipOne могли наследовать эти возможности. Для этого нам следует воспользоваться методом Object.registerClass():
Object.registerClass("clipOne", CustomClass);
Теперь все экземпляры фильма-символа clipOne будут реагировать на , rollOut и enterFrame так, как предписывает CustomClass. Технически они более не считаются экземплярами – вместо этого они стали экземплярами CustomClass.
Примечание При описании методов – обработчиков событий использовался термин this ; в итоге он будет адресной ссылкой на каждый конкретный экземпляр, унаследовавший функции.
Если вдруг необходимость в наследовании экземплярами clipOne функциональности класса CustomClass отпадет, можно применить следующий синтаксис:
Object.registerClass("clipOne", null);
Регистрация фильма-символа как класса и ее отмена не имеет эффекта для существующих экземпляров данного фильма-символа, она действует только для новых экземпляров, помещаемых в фильм – прямо на монтажный стол в процессе разработки либо динамически, посредством duplicateMovieClip() или . Существующие экземпляры сохраняют текущую функциональность. Чтобы вы лучше поняли этот принцип, давайте рассмотрим пример. Допустим, в кадре 1 вашего фильма вы описали CustomClass. В этот же кадр вы поместили экземпляр фильма-символа clipOne и назвали его myInstance. В кадр 2 вы поместили действие, регистрирующее clipOne как CustomClass:
Object.registerClass("clipOne", CustomClass);
Теперь, хотя мы зарегистрировали clipOne как CustomClass, экземпляр этого фильма-символа myInstance (помещенный в кадр 1) не унаследует функциональность CustomClass, поскольку этот экземпляр появился в фильме (в кадре 1) до того, как был вызван метод registerClass() (в кадре 2). Только те экземпляры, которые будут помещены в фильм с этого момента – вручную или динамически – будут наследовать возможности CustomClass.

Но что же делать, если необходимо поместить экземпляры clipOne в кадр 1, притом так, чтобы они унаследовали возможности CustomClass, описанного в этом же кадре? Об этом вы узнаете, выполнив упражнение этого раздела. Важно уяснить, как это работает, в противном случае даже долгие часы мучений ни к чему не приведут.
Фильм-символ (как тот же clipOne ) может быть связан (зарегистрирован) только с одним классом в каждый момент времени. Если вы, зарегистрировав фильм как класс, затем регистрируете его как другой класс, то эта последняя регистрация заменит предыдущую.
Однако это не означает, что несколько фильмов нельзя связать с одним классом – это как раз можно. Просто вызываете опять метод registerClass() – столько раз, сколько нужно:
Object.registerClass("clipTwo", CustomClass);
Object.registerClass("myClip", CustomClass);
Object.registerClass("anotherClip", CustomClass);
В результате этих действий экземпляры фильмов-символов clipTwo, myClip и anotherClip будут наследовать возможности CustomClass.
Совет Если вы хотите ассоциировать со своим классом только отдельный экземпляр фильма-символа, просто назначьте свойству _proto_ объект-прототип этого класса:
myMovieClip._proto_ = CustomClass.prototype;
В этом случае экземпляр myMovieClip унаследует возможности CustomClass. Такое изменение свойства _proto_ отражается на экземпляре немедленно.
Итак, в следующем упражнении мы создадим собственный класс, настроим его функциональные возможности, а затем, с помощью метода registerClass(), назначим фильм-символ из библиотеки объектом нашего класса.
С этим проектом мы работали в предыдущих упражнениях, его элементы вам знакомы. Однако здесь мы несколько изменили структуру проекта. Теперь проект содержит две сцены, идентичные во всем, кроме одного: сцена 1 содержит кнопку, при нажатии которой происходит переход к сцене 2, а во второй сцене, наоборот, имеется кнопка для перехода в сцену 1. Кроме того, в кадр 1 сцены 1 добавлено действие stop(), чтобы пользователь мог переключаться между сценами с помощью кнопок.
Кнопки справа от экземпляра bigHead теперь заменены четырьмя экземплярами фильма-символа . В среде разработки эти экземпляры выглядят одинаково (изображения рта), однако на самом деле фильм-символ содержит все изображения, которые мы использовали ранее для кнопок (глаза, нос, очки). К тому времени, как мы закончим программировать этот проект и проиграем его, эти четыре экземпляра будут выглядеть так же, как те кнопки в предыдущих упражнениях.
В этом упражнении мы сделаем так, чтобы фильм-символ – а значит, и эти четыре экземпляра – унаследовали функции нового класса, который мы создадим. В результате эти четыре экземпляра станут как бы усовершенствованными версиями тех кнопок, которые мы использовали прежде.
Четыре экземпляра имеют следующие имена (сверху вниз): mouth, nose, eyes и . Запомните эти имена: в построении проекта они будут важны. Скрипты в этом упражнении мы будем помещать в основном на монтажный стол фильма-символа . Прежде, чем заняться этим, давайте откроем библиотеку и посмотрим, что представляет собою этот фильм-символ.
В открывшемся диалоговом окне Свойства связи (Linkage Properties) вы видите, что фильм символ имеет идентификатор (имя) "

Примечание Подробнее о настройках связи смотри в Уроке 14 – Динамическое управление фильмами-символами.
Монтажный стол этого клипа содержит четыре слоя, в каждом из которых – 40 кадров. В слое Labels имеется четыре mouth, nose, eyes и . Каждая из них обозначает иное графическое изображение (в слое Graphics ), соответствующее названию метки. Слой
_global.Custom1 = function(){
}
Custom1.prototype = new MovieClip();
Первые две строки создают новый класс под названием Custom1. Как видите, описание класса предельно простое. Следующая строка устанавливает наследование, в результате которого класс Custom1 получит все функциональные возможности класса . Теперь мы опишем особые возможности, присущие классу Custom1.
Custom1.prototype.scale = function(amount){
this._xscale = amount;
this._yscale = amount;
}
Этот блок кода описывает для класса Custom1 новый метод под названием . Метод будет управлять масштабированием экземпляра, основываясь на значении , передаваемом методу при вызове. Скоро мы им воспользуемся.
Примечание В этом скрипте (и в последующих) this в итоге станет ссылкой на тот экземпляр, который унаследует возможности класса. Так, если экземпляр с именем myClip станет наследником Custom1, действие myClip. ; установит масштаб в 50 процентов для этого экземпляра.
Custom1.prototype.onLoad = function(){
this.gotoAndStop(this._name);
}
В этой части скрипта мы создаем метод - обработчик события onLoad – он описывает, что должно происходить при загрузке экземпляров данного класса. Действие gotoAndStop() будет перемещать каждый экземпляр к метке, соответствующей его имени. Четыре экземпляра фильма-символа унаследуют эту способность класса. Имена этих экземпляров – mouth, nose, eyes и – такие же, как имена четырех меток на монтажном столе данного фильма-символа. Таким образом, при загрузке (onLoad) экземпляр перейдет к метке, соответствующей его имени ( this._name ). В результате после начала фильма четыре экземпляра будут выглядеть так же, как прежние четыре кнопки.

Custom1.prototype.onPress = function(){
thisX = this._x;
thisY = this._y;
startDrag(this, true);
this.scale(70);
}
В этой части скрипта использован метод-обработчик события onPress, он устанавливает, что должно происходить с экземплярами данного класса при нажатии на них кнопки мыши. Первые две строки фиксируют текущие координаты экземпляра по x и y и помещают значения в переменные с именами thisX и thisY. Следующее действие делает экземпляр перетаскиваемым. В последней строке используется метод , созданный нами на предыдущем шаге – в данном случае он уменьшает экземпляр до 70 процентов от нормального размера.
Custom1.prototype.onRelease = function(){
stopDrag();
if (this.hitTest("_root.bigHead")){
this._x = thisX;
this._y = thisY;
_root.bigHead[this._name] = true;
this.scale(100);
this.enabled = false;
}else{
this._x = thisX;
this._y = thisY;
this.scale(100);
}
}
В этой части скрипта применен метод-обработчик события onRelease, описывающий, что должно происходить с экземплярами данного класса при отпускании нажатой кнопки мыши. Первым делом прекращается перетаскивание экземпляра. Затем вступает в действие оператор if, выполняющий один из двух наборов инструкций в зависимости от того, где оказался экземпляр в результате перетаскивания. Для проверки условия использован метод hitTest() – с его помощью выясняется, оказался ли экземпляр после перетаскивания на экземпляре bigHead. Если это так, то выполняется первый набор действий. Два первых действия перемещают экземпляр в координаты со значениями thisX и thisY – то есть в изначальное положение. В следующем действии свойству экземпляра bigHead, имеющему такое же название, как имя текущего экземпляра, присваивается значение true. Например, если нажатый и отпущенный экземпляр имеет имя nose, то данная строка расшифруется так:
_root.bigHead.nose = true;
Таким образом, изменяется значение свойства nose экземпляра bigHead – а оно, как вы помните, отслеживается, так что в результате этого изменения станет видимым нос на большом лице (мы запрограммировали это в одном из предыдущих упражнений.
Следующее действие возвращает экземпляру первоначальный размер, а последнее – "отключает" экземпляр, так что он не будет более реагировать на событие onPress (это относится только к данному конкретному экземпляру).
Секция else выполняется, если экземпляр в результате перетаскивания не попал на bigHead. В этом случае выполняется всего три действия. Экземпляр попросту возвращается в начальное положение и принимает нормальный размер. После этого его можно снова нажимать и перетаскивать, и тогда снова оператор if предпримет соответствующие действия.
Программирование функциональных возможностей нашего класса закончено. Осталось ассоциировать с этим классом фильм-символ , чтобы его экземпляры наследовали функции класса Custom1.
Object.registerClass("icons", Custom1);
Мы регистрируем фильм-символ (его Custom1 ; теперь экземпляры фильма будут наследовать все свойства и методы этого класса.
С началом воспроизведения фильма вы обнаружите, что у нас проблемы – проект не функционирует. Четыре экземпляра фильма-символа , похоже, не унаследовали возможности Custom1, как мы планировали. Но если нажать кнопку Go to registerClass() не имел эффекта, а после переключения сцен заработал. Ладно, давайте сперва внесем нужные исправления, а потом разберем, в чем было дело.
#initclip // код, введенный на предыдущих шагах #endinitclip
Эти действия заключают между собой так называемый блок инициализации. Что это такое, мы сейчас разъясним, но прежде давайте проверим, как работает фильм теперь.

На сей раз вы увидите, что наши четыре экземпляра функционируют как должно: работает и перетаскивание, и все остальное; экземпляры обладают всеми возможностями класса Custom1. Если перейти в сцену 2, то и там все будет в порядке. Так в чем же тут дело?
Когда мы тестировали фильм в прошлый раз, эффект регистрации фильма-символа как представителя класса Custom1 явно не имел места, а теперь все сработало. Давайте разберем по пунктам, как развивались события при первом тестировании фильма:
Custom1, и здесь же находится registerClass() ).Icons будут наследовать возможности класса Custom1.Теперь понятно, почему не работали четыре экземпляра – они появились на сцене (третий пункт) до того, как произошла регистрация (пятый пункт). А, как вы помните, регистрация и ее отмены не имеет эффекта для существующих экземпляров фильма-символа – только для новых. Существующие экземпляры сохраняют прежние функциональные возможности. Вот и в нашем фильме к моменту регистрации четыре экземпляра уже были созданы, и потому ничего не унаследовали. Ну, а как же экземпляры во второй сцене? Они-то работали. Верно, работали, поскольку переход в сцену 2 (и создание экземпляров в ней) можно считать седьмым пунктом в нашем перечне событий. Регистрация уже состоялась, и на экземпляры сцены 2 подействовала. Переход обратно в сцену 1 будет восьмым пунктом; экземпляры созданы заново, и регистрация тоже дала нужный результат. Ну вот, мы выяснили, почему так странно (на первый взгляд) работал первоначальный тест. А каким же образом решили проблему два действия, которые мы добавили?
Нам требовался механизм, который обеспечил бы наследование четырьмя экземплярами функций класса Custom1 – то есть выполнил регистрацию – до загрузки кадра 1 и создания этих экземпляров. Так вот, действия # и #endinitclip – и есть такой механизм. Это специальные команды, которые используются только в фильмах-символах (и только в их кадре 1); они позволяют управлять процессом инициализации фильма – иначе говоря, порядком выполнения действий. Вот как это работает.
Помните, в диалоговом окне Свойства связи (Linkage Properties) для фильма-символа (из библиотеки) была установлена опция "Экспорт в первый кадр" (", обнаружил в его первом кадре блок # – #endinitclip и должен был немедленно выполнить все действия заключенные в этом блоке. Таким образом этот скрипт был выполнен до загрузки кадра 1 основного монтажного стола, и порядок событий получился таким:
#initclip – #endinitclip (в этом блоке как раз находится описание класса Custom1, а также registerClass() ).Icons будут наследовать возможности класса Custom1.Icons ).#initclip – #endinitclip ).И в результате процесс регистрации состоялся (второй пункт) до того, как были созданы экземпляры кадра 1 (пятый пункт). Наши четыре экземпляра благополучно унаследовали все, что нужно – как и показало второе тестирование.
Конечно, не обязательно программировать создание класса и регистрацию фильма-символа как объекта этого класса именно тем способом, который был описан в данном упражнении. Главное – чтобы вы понимали порядок выполнения скриптов и то, как этот порядок повлияет на процесс регистрации, а способов можно выбрать в зависимости от обстоятельств.
Примечание Показанную здесь схему инициализации (с блоком # ) используют все компоненты
Примечание Если для клипа, содержащего блок #, не установлена опция "Экспорт в первый кадр" ("
Давайте потренируемся еще на одном примере, добавим в наш проект последний скрипт.

Этот клип изображает у нас на сцене головы. Мы присваиваем ему идентификатор, чтобы можно было его зарегистрировать как объект еще одного нового класса, который мы сейчас создадим.
_global.Custom2 = function(){
}
Custom2.prototype = new MovieClip();
Custom2.prototype.onEnterFrame = function(){
this._rotation += 5;
}
Object.registerClass("Icons", Custom2);
Object.registerClass("headClip", Custom2);
В шести первых строках создается новый класс под названием Custom2 ; его экземплярам дается способность поворачиваться на 5 градусов каждый раз при наступлении события enterFrame. Две последние строки регистрируют фильмы-символы и HeadClip как объекты класса Custom2 – после нажатия кнопки их экземпляры унаследуют возможности этого класса. Заметьте – мы поместили эти действия перед действием nextScene(): это сделано для того, чтобы зарегистрировать клипы до перехода в следующую сцену, в которой создаются экземпляры этих символов. Если бы мы поступили наоборот и поместили этот скрипт после действия nextScene(), то сначала произошел бы переход во вторую сцену, были бы созданы экземпляры, и только потом состоялась бы регистрация клипов. При таком порядке событий регистрация не имела бы эффекта – ведь экземпляры клипов уже существуют.
Итак, при нажатии этой кнопки и выполнении скрипта регистрация клипа как объекта класса Custom2 отменяет предыдущую связь его с классом Custom1 (которую мы создавали и обсуждали в предыдущих упражнениях). Таким образом, изначально, в сцене 1, экземпляры клипа будут функционировать как объекты класса Custom1, но после нажатия этой кнопки все последующие экземпляры будут наследовать функциональность Custom2 (как и экземпляры фильма-символа HeadClip ).
Этот скрипт демонстрирует, что как объявление нового класса, так и регистрация могут происходить в любом месте фильма. Соблюдая правильный порядок событий при регистрации, вы можете осуществлять ее, когда и как вам удобно.
Сразу после начала воспроизведения фильма четыре экземпляра справа от bigHead ведут себя как объекты класса Custom1. Нажмите кнопку перехода в сцену 2, и эти экземпляры, как и экземпляры фильма-символа HeadClip, станут функционировать, как объекты класса Custom2. Их больше нельзя перетаскивать, они просто вращаются, как предписывает класс Custom2.

Уф, даже не верится – этот урок закончился. Долгим он был, зато вы так много узнали об объектах, наследовании, классах, подклассах, методах и о многом другом. Теперь вам по силам взяться за разработку весьма сложных проектов. А кроме того, все эти знания помогут вам в последующих уроках этой книги.
Нам,
В этом уроке, программируя приложение
В этом уроке:

В этом уроке мы покажем вам, как работают объекты и как их создавать, так что впоследствии вы сможете строить новые
классы объектов с нуля, равно как и расширять возможности существующих.
На выполнение этого урока требуется примерно два с половиной часа.
Файлы-носители:
Нет
Стартовые файлы:
Lesson06/Assets/object1.fla Lesson06/Assets/class1.fla Lesson06/Assets/objectProject1.fla Lesson06/Assets/registerClass1.fla
Законченные проекты:
object2.fla class4.fla objectProject4.fla registerClass2.fla
В Уроке 4 мы познакомили вас со встроенными объектами
Чувствую, сейчас кто-нибудь скажет: "Мы же уже создавали объекты и модифицировали их в четвертом уроке, теперь что, опять то же самое?" Да, в Уроке 4 вы создавали экземпляры Array, Color, Date, String, и т.д.), но мы не говорили ни о том, как создать свой собственный
Примечание Экземпляры объектов часто называют просто объектами. Как правило, говоря об объектах, подразумевают именно экземпляры.
Концепция построения и работы объектов есть вещь довольно-таки абстрактная, поэтому упражнения в нескольких следующих разделах будут несколько отличаться от прочих. В большинстве упражнений из этой книги вы имеете дело с вещами зримыми и осязаемыми – обычно это графические элементы, которыми вы манипулируете в проекте. В следующих упражнениях действие будет происходить по большей части за сценой. Здесь нам придется пользоваться действием
trace(_root._totalframes);
Если это действие поместить в первый кадр фильма, состоящего из 47 кадров, то при тестировании фильма автоматически откроется окно Выход ( Output ) и в нем отобразится:
47

Это простое действие очень пригодится нам при изучении работы объектов.
Что ж, начнем.
Примечание Если вы знакомы с
Объекты
person1 = new Object();
Эта строка создает новый экземпляр родового объекта с именем person1.
Примечание Объект Object (использованный здесь) не имеет специального применения – это родовой объект. Таким образом, в приведенной строке фигурирует экземпляр объекта Object (а не новый класс), который мы создали, чтобы продемонстрировать роль объекта как контейнера. В следующем уроке вы узнаете, что объект Object имеет более широкое применение.
На данный момент person1 – никакая не персона, вообще никто: мы не задали для нее никаких характеристик – ни пола, ни возраста, вообще ничего.
person1.gender = "male"; trace (person1.gender); trace (person1.age);
Теперь объект person1 имеет свойство, под названием gender, со значением "male". Еще мы добавили пару действий , чтобы посмотреть, какие значения имеются в объекте person1. Давайте посмотрим, что у нас есть на данный момент.
Откроется окно Выход (Output), и в нем отобразится:
male undefined
Первое из действий вернуло значение "male", поскольку именно такое значение имеет person1.gender. Второе действие вернуло значение для person1.age – это значение мы не присвоили, оно не определено.
Давайте добавим в person1 еще несколько свойств и значений.
person1 = new Object(); person1.age = 16; person1.legs = 2; person1.name = "Justin";
Как видим, person1 приобретает все больше черт. Теперь она имеет все самое необходимое, чтобы войти в общество.

На данный момент любой скрипт может получить доступ к данным, связанным с person1 – мы продемонстрируем это на следующем шаге.
if (person1.age >= 21){
trace("Я иду на дискотеку");
}else{
trace("Я иду в МакДональдс");
}
Этот оператор проверяет значение person1.age, чтобы определить дальнейший образ действий.
Откроется окно Выход (Output), и в нем отобразится:
Я иду в МакДональдс
Оператор if выдает именно такой результат, поскольку person1.age имеет значение 16.
Давайте добавим еще кое-что в наш объект person1 – продемонстрируем, что объект может содержать в себе другой объект. Эта концепция вам, в принципе, знакома – вспомните вложенные монтажные столы (это ведь не что иное, как объект внутри другого объекта ).
person1.head = new Object(); person1.head.eyes = 2; person1.head.memories = new Array();
Итак, наша персона person1 получила (наконец-то!) голову – head. Это произошло в результате создания нового экземпляра объекта Object и помещения его внутрь объекта person1. Объекту head мы дали свойство eyes, имеющее значение 2. Мало того, внутрь объекта head мы поместили объект Array (c именем ).

Как видите, мы поместили один объект ( ) внутрь другого объекта ( head ), который, в свою очередь, находится в объекте person1. Мы могли бы поместить непосредственно в person1, вот так:
person1.memories = new Array();
Мы так не поступили, и на то были веские причины, о которых мы скоро расскажем.
Совет Можно поместить в person1.head существующий массив – с именем, скажем, myThoughts (а не создавать новый, как было показано), для этого следовало бы использовать такой синтаксис: person1.head..
Что ж, давайте добавим несколько воспоминаний в массив .
person1.head.memories[0] = "Я родился, пришел в этот мир."; person1.head.memories[1] = "Я узнал слово 'НЕТ'. Очень продуктивный день."; person1.head.memories[2] = "Я узнал, что если малость поору, то получу, что хочу.";
Эти три строки помещают данные в массив , находящийся внутри head объекта person1.
trace(person1.head.memories[1]);
Откроется окно Выход (Output), и в нем отобразится такая надпись:
Я узнал слово 'НЕТ'. Очень продуктивный день.
Это – значение, которое содержит person1.head..
Все, что мы запрограммировали, мы используем в следующем упражнении. Пока что запомните, что объекты – это контейнеры, способные хранить не только простые "порции" данных, но даже другие объекты. Смысл тут тот же, что при помещении переменной в экземпляр фильма-символа, или при создании на монтажном столе экземпляра Color, или другого объекта (см. Урок 4 – "Объекты и их использование"). Вы помещаете объект (скажем, экземпляр объекта Color ) на монтажный стол (внутрь экземпляра объекта ). Создавая на монтажном столе переменную, вы на самом-то деле даете новое свойство объекту .
Когда вы помещаете монтажный стол (то есть, экземпляр объекта ) на другой монтажный стол (другой экземпляр) – вам приходилось делать это с фильмами-символами – вы создаете
person1.head.memories
Здесь head является дочерним объектом по отношению к person1, а – head, и person1 (таким образом, head – родитель , а person1 – предок head и ).
Теперь внимание: следует помнить о том, что если что-то происходит с родителем, то же случается и с его потомками – что мы и продемонстрируем в следующем упражнении.
delete person1.head; trace(person1.head); trace(person1.head.memories[1]); trace(person1.name);
Первое действие удаляет head из person1. Последующие действия позволяют нам увидеть, как это повлияло на наш объект.
Откроется окно Выход (Output), и в нем отобразится:
undefined undefined Justin
Мы видим, что person1.head теперь , как и , находившийся внутри head. Будучи потомком head массив был удален вместе со своим родителем. Однако на прочие свойства объекта person1 это не повлияло – что и продемонстрировало последнее действие , вернув значение person1.name.

Как видите, правильно сконструированный объект реагирует на внешние обстоятельства почти так же, как объекты из реальной жизни. А в соединении с действиями, вызывающими визуальные изменения в фильме, (скоро мы до этого дойдем) объекты способны и на большее.
person1.head = new Object(); trace(person1.head); trace(person1.head.memories[1]);
Первая строка вновь добавляет объект head к person1. Последующие действия позволяют нам увидеть результат. Учтите, что эти три строки скрипта мы поместили после четырех строк, добавленных на первом шаге. Таким образом, при тестировании проекта окно Выход вновь покажет нам результат удаления person1.head ; но на этот раз за ним будет следовать результат возвращения свойства head.
Откроется окно Выход (Output), и в нем отобразится:
undefined undefined Justin [object Object] undefined
Три первых выведенных строки – те же, что мы видели на шаге 2. А две последние показывают, что получилось после возвращения person1 свойства head. В результате person1.head отображено как [object Object], и это значит, что объект head опять существует (и является экземпляром объекта Object), однако person1.head. остался . Причина в том, что массив person1.head. был удален до этого (в результате удаления person1.head ), и "забыт" – выгружен из памяти компьютера (вместе, кстати, со свойством eyes, которое также было частью объекта head ). Когда мы вновь добавили свойство head, компьютер уже не "вспомнил" ни массива , ни свойства eyes. Чтобы восстановить их, пришлось бы создавать эти элементы заново и заново помещать в них данные.
Наша работа с этим файлом завершена. В следующем разделе мы продолжим изучение объектов – и теперь подойдем с другой стороны.
До сего времени мы использовали экземпляр родового объекта Object для демонстрации принципов действия нашего объекта person1. Теперь настало время перейти на следующий уровень механики объектов: создать собственный класс, который мы опишем в этом упражнении как класс Person. Этот класс послужит образцом, "чертежом" для создания новых экземпляров "персон" – вы убедитесь, что такой способ более эффективен для создания person1 и ему подобных.
Примечание В предыдущих упражнениях мы использовали объект Object главным образом ради демонстрации общей концепции объектов. На самом деле объект Object может найти действительно ценное применение в реальных проектах, хотя это – весьма общее решение, которое подойдет не для всякого приложения.
Как и в предыдущих упражнениях, здесь, изучая "теоретическую часть", мы будем частенько пользоваться действием .
Person = function(){
}
Да, это очень похоже на
Примечание В ходе дальнейшего обсуждения темы термины конструктор функции и описание класса будут означать одно и то же.
Обычное Person:
Person(); //выполняется как функция person1 = new Person(); //создается экземпляр класса Person
Примечание Хотя описание класса выглядит, как функция, его следует рассматривать именно как описание класса. Использовать один и тот же блок кода обоими способами не советуем.
Снова повторим, что описание класса мы рассматриваем как чертеж, или шаблон, для создания экземпляров этого класса. Внутри фигурных скобок мы помещаем характеристики, описывающие этот Person не имеет никаких характеристик (свойств), поэтому давайте опишем этот класс, как в нашем предыдущем примере.
Person = function(){
this.name = "Justin";
this.age = 16;
this.legs = 2;
this.head = new Object();
this.head.eyes = 2;
this.head.memories = new Array();
}
Наше описание класса завершено. Далее мы поговорим о том, как его использовать и что означает this в контексте описания класса. Также мы покажем, как создавать экземпляры нашего нового класса.
Совет Возможно, вы обратили внимание, что в описании имя нового класса Person начинается с заглавной буквы. Хотя это не обязательно, такова общая практика. Имена экземпляров класса, напротив, начинаются обычно с маленькой буквы. Соблюдение этого простого правила позволит сделать ваши скрипты более
person1 = new Person();
Этим создается экземпляр объекта Person с именем person1.

Когда откроется окно Выход ( Output ), в нем отобразится следующая информация:
Variable _level0.person1 = [object #2] {
name:"Justin",
age:16,
legs:2,
head:[object #3, class 'Object'] {
eyes:2,
memories:[object #4, class 'Array'] []
}
}
Это – информация об экземпляре person1, который мы создали действием, добавленным на предыдущем шаге. Как определяет наш класс Person, экземпляр person1 имеет имя "Justin", возраст 16, две ноги и так далее.
Важно понимать, что команда person1 = new Person() ; создает экземпляр, свойства которого определены классом Person. Все происходит очень просто: выполняя эту строку кода, Person. Тогда создается временный объект (объект Activation) и отсылается к описанию класса Person, запрограммированному ранее (все это происходит автоматически и внешне никак не проявляется). Объект Activation, поначалу совершенно бесформенный, "проходит" через описание класса, где получает различные свойства класса ( name, age, legs и так далее). В ходе этого процесса временный объект Activation для адресации пользуется термином this, как указано в описании класса. Например, в описании класса встретилась строка:
this.age = 16;
Это значит, что когда объект Activation будет проходить через нее, его свойству age должно быть присвоено значение 16. Процесс создания экземпляра можно представить как движение объекта Activation по сборочной линии конвейера – к нему добавляются по очереди части и он движется дальше. Когда процесс сборки завершен, объект активации получает имя (в данном случае person1 ), и все – он готов к использованию.

Итак, мы описали класс Person и знаем, как создавать его экземпляры, однако все эти экземпляры будут совершенно идентичными. Все дело в том, что так был определен наш класс Person – все его экземпляры будут иметь имя "Justin", возраст 16 и так далее. Хотелось бы, чтоб наш класс Person был способен на что-то большее, чем творить собственных клонов, а потому давайте внесем в описание класса некоторые изменения, чтобы можно было создавать экземпляры с различающимися значениями свойств.
Person = function(name, age){
this.name = name;
this.age = age;
this.legs = 2;
this.head = new Object()
this.head.eyes = 2;
this.head.memories = new Array();
}
Подобно объявлению обычной функции, в нашем описании класса Person теперь указано два аргумента: name и age. Обратите внимание – те же самые значения указаны теперь и в первых двух строках описания класса. Это позволит нам создавать экземпляры класса Person, имеющие уникальные значения свойств.
person1 = new Person ("Justin", 16);
person2 = new Person ("Derek", 29);
Эти два действия создают два экземпляра класса Person. Эти экземпляры имеют разные значения свойств name и age – обновленное описание класса Person позволяет это. Чтобы задать свойства нового экземпляра, мы можем передавать конструктору строки, числа и даже имена объектов.
Теперь хотелось бы упростить создание экземпляров класса Person, чтобы это можно было сделать с любого монтажного стола без указания пути, подобно
_global.Person = function(name, age){
this.name = name;
this.age = age;
this.legs = 2;
this.head = new Object()
this.head.eyes = 2;
this.head.memories = new Array();
}
Обратите внимание на _global в первой строке скрипта: именно это слово позволит создавать экземпляры класса Person с любого монтажного стола, просто используя такой конструктор:
new Person();
Если бы слова _global не было, и описание класса находилось бы, скажем, на основном монтажном столе, то с других монтажных столов пришлось бы использовать следующий синтаксис:
new _root.Person();
Примечание Даже если вы, используя _global, сделаете класс доступным для всех монтажных столов без адресации, отдельные экземпляры все-таки будут находиться на монтажном столе, содержащем скрипт, создавший этот экземпляр. Допустим, вы создаете экземпляр объекта Person с именем myBuddy, причем скрипт, создающий экземпляр, находится в _root.myMovieClip. Тогда абсолютный адрес экземпляра myBuddy будет _root.myMovieClip.myBuddy. Конечно, при желании можно сделать глобальным и экземпляр, использовав такой синтаксис: _global.myBuddy = new Person("Dennis", 27);.
В следующем упражнении мы продолжим работать с этим файлом. Мы теперь умеем создавать экземпляры класса Person, однако этого мало – нужно подумать о его "тонкой настройке" в случае необходимости, а для этого требуется освоить такую мощную штуку, как объект-прототип (звучит жутковато, правда?).
Когда рождается ребенок, мы ожидаем, что у него будет голова, два глаза, два уха, две ноги и так далее. Все это мы ожидаем увидеть у всех детей, поскольку эти признаки жестко зашиты в нашем генетическом коде. То же самое применимо и к объектам, создаваемым Person имена и возраст могут варьироваться, но прочие характеристики являются общими для всех экземпляров. Давайте, рассуждая таким образом, внесем еще изменения в описание нашего класса Person.
_global.Person = function(name, age){
this.name = name;
this.age = age;
}
Person.prototype.legs = 2;
Person.prototype.head = new Object()
Person.prototype.head.eyes = 2;
Person.prototype.head.memories = new Array();
Здесь, как видите, мы вынесли некоторые свойства за пределы формального описания класса и присоединили их к объекту-прототипу класса Person – специальному объекту, связанному с классом. Этот объект (создаваемый автоматически, когда описывается новый класс) содержит свойства и значения, универсальные для данного класса. Можете считать, что этот объект – вроде как прицеп к описанию класса, специальное приложение.

Примечание Все
Объект-прототип класса не будет содержать никаких значений, пока мы их не запрограммируем – поэтому мы и добавили этому объекту три свойства (и присвоили им значения) сразу после описания класса. Эти свойства объекта-прототипа автоматически наследуются всеми экземплярами данного класса, и их значения доступны точно так же. Очень важно уяснить принцип работы объекта-прототипа, поэтому давайте об этом поговорим.
Когда создается новый экземпляр класса Person, описание класса придает ему два свойства – name и age. Вот иллюстрация доступа к одному из этих свойств:
myVariable = person1.age;
Первым делом person1, чтобы определить, имеется ли у этого экземпляра свойство age. Видя, что оно есть (это свойство было присоединено при активации экземпляра – проходе через описание класса), myVariable. Теперь давайте рассмотрим такую строку скрипта:
myVariable = person1.legs;
Вы помните, что описание класса Person теперь не включает свойства legs, тем не менее, myVariable получит значение 2. Дело вот в чем: при таком сценарии legs. Не найдя такого, legs у этого экземпляра, надо посмотреть в объекте Person., может, там есть такое". Тут Person. существует и имеет значение 2, стало быть, и person1.legs равно 2. Это значение myVariable.
Итак,

Вы можете подумать: ведь все эти свойства прекрасно работали, будучи частью описания класса (как в предыдущем упражнении); зачем же морочить голову еще и прототипами? Дело в том, что объект-прототип позволяет распространять изменения на весь класс – то есть на все экземпляры этого класса. Если изменить значение какого-либо свойства в объекте-прототипе класса, то все экземпляры этого класса – вновь создаваемые и существующие – унаследуют это новое значение (за одним исключением, о котором чуть позже). Чтобы вы еще лучше усвоили эту концепцию, давайте немного поупражняемся.
person1 = new Person ("Justin", 16);
person2 = new Person ("Derek", 29);
trace (person1.legs);
trace (person2.legs);
Первые две строки создают два экземпляра объекта Person. Действия выводят значение свойства legs для каждого экземпляра.
Откроется окно Выход (Output), и в нем отобразится следующее:
2 2
Как видите, person1 и person2 имеют одинаковое значение свойства legs. Мы можем сделать изменение для всего класса (то есть изменить значение для всех экземпляров), поменяв значение свойства legs объекта Person.. Давайте так и сделаем.
Откроется окно Выход (Output), и в нем отобразится следующее:
5 5
Как видите, простое изменение одного значения в объекте Person. привело к тому, что и person1, и person2 получили новое значение свойства legs. Если бы у нас было сто экземпляров, все они получили бы это новое значение. Добавление, удаление или изменение свойств объекта-прототипа класса немедленно отражаются на всех экземплярах этого класса (единственное исключение, о котором мы уже упоминали, мы обсудим в следующем разделе).
Итак, экземпляры объекта могут иметь как уникальные свойства, так и универсальные (одинаковые для всех экземпляров данного класса). Например, мы все имеем разные имена и возраст (свойства, заложенные в описании класса), но также мы имеем некоторые общие признаки – две ноги, голова, и на ней два глаза (свойства, устанавливаемые объектом-прототипом).
Мы продолжим работу с этим файлом в следующем упражнении.
Как упоминалось в предыдущем разделе, при попытке обращения к значению свойства – например, присвоении myVariable значения person1.legs (myVariable = person1.legs;) – person1, созданный нами в предыдущем упражнении, имеет следующие свойства:
name = "Justin" // уровень экземпляра age = 16 // уровень экземпляра legs = 2 // уровень прототипа head = new Object() // уровень прототипа head.eyes = 2 // уровень прототипа head.memories = new Array() // уровень прототипа
Почему важно различать свойства уровней экземпляра и прототипа? В ситуации, когда свойства уровня экземпляра и уровня прототипа имеют одно и то же имя, значение свойства уровня экземпляра перекрывает значение уровня прототипа (хотя это относится только к данному экземпляру).
Допустим, экземпляр person1 получает свойство legs от объекта Person., поскольку при создании экземпляра это свойство не было присвоено. Однако если мы присвоим значение этому свойству, обратившись непосредственно к экземпляру – например, person1.legs = 1 ; – мы создадим у person1 свойство legs уровня экземпляра, и его значение перекроет значение, установленное прототипом. У всех прочих экземпляров останется две ноги – за исключением person1, который теперь имеет только одну. Значение уровня прототипа попрежнему существует и для этого экземпляра, только теперь оно невидимо, перекрыто свойством уровня экземпляра с таким же именем.

Мы можем удалить свойство legs уровня экземпляра – вот таким образом:
delete person1.legs;
В результате person1 вновь станет отражать свойство legs (и его значение), установленное объектом-прототипом класса Person.
Еще один важный аспект, касающийся свойств уровня экземпляра – то, что благодаря им экземпляры могут иметь свои уникальные свойства вдобавок к полученным из описания класса и унаследованным от прототипа. Чтобы добавить такое свойство, нужно просто обратиться непосредственно к экземпляру. Например:
person2.favoriteFood = "Pizza";
Теперь person2 станет единственным экземпляром, имеющим свойство favoriteFood. Конечно, мы можем запросто добавить такое свойство и прочим экземплярам (присвоив его напрямую либо добавив в объект-прототип), но пока мы этого не сделаем, свойство favoriteFood будет только у экземпляра person2.
Примечание Если добавить свойство в описание класса, то это свойство будут получать лишь вновь создаваемые экземпляры, начиная с этого момента, но не те, что уже существуют. Причина в том, что конструктор функции (описание класса) присоединяет свойства к экземплярам лишь в процессе их создания.
Свойства уровня экземпляра позволяют индивидуализировать экземпляры, не принося в жертву преимущества
Наш класс Person приобретает все большую функциональность: мы можем создавать его экземпляры, указывая для них имена и возраст, и еще они могут наследовать свойства объекта-прототипа. Но, как в реальной жизни, могут существовать еще и классы внутри нашего класса Person: доктора, юристы, Person (имя, возраст, ноги, голова), но имеют и свои, уникальные. Если, например, вы создаете класс Доктор, он должен обладать такими особыми характеристиками, как специальность, звание (" Dr."), и так далее, и в то же время – всеми признаками класса Person.
_global.Doctor = function(almaMater){
this.almaMater = almaMater;
}
Doctor.prototype.title = "Dr.";
Здесь мы создаем конструктор функции, описывающий новый Doctor. Каждый новый экземпляр этого almaMater, значение которого передается конструктору функции при создании экземпляра. Также мы придали классу Doctor прототип со свойством title, имеющим значение "Dr.". Поскольку это звание носят все доктора, мы можем поместить его в объект-прототип класса Doctor.

Давайте создадим экземпляр нашего нового класса.
frankenstein = new Doctor("Transylvania");
trace(frankenstein.almaMater);
trace(frankenstein.title);
trace(frankenstein.legs);
Первое действие создает новый экземпляр класса Doctor с именем frankenstein. Последующие действия
Откроется окно Выход (Output), и в нем отобразится следующее:
Transylvania Dr. undefined
Как видите, со свойством almaMater все в порядке (это свойство уровня экземпляра, и оно было добавлено при создании экземпляра), и со свойством title тоже (благодаря объекту Doctor. ), а вот свойство legs не существует.
Нашей целью было сделать класс Doctor подклассом класса Person, чтобы все экземпляры класса Doctor наследовали такие свойства, как legs, head и так далее. Только это не произойдет само собою – где-либо в нашем скрипте мы должны сообщить Doctor есть Person.
Doctor.prototype .title = "Dr." :Doctor.prototype = new Person();
Эта строка скрипта создает связь между классами Doctor и Person. Она указывает, что объект-прототип класса Doctor должен наследовать объекту-прототипу Person.
В результате все экземпляры класса Doctor будут отражать свойства не только Doctor., но также и Person.. Обратное неверно; экземпляры класса Person будут отражать только свойства Person.. Теперь Doctor – Person, а Person – Doctor.
Существует причина, по которой мы поместили нашу последнюю строку перед строкой устанавливающей свойство title объекта Doctor.. При выполнении добавленной нами строки объект Doctor., по существу, полностью очищается, все свойства, которые в нем могли быть, заменяются свойствами объекта Person.. Если бы мы добавили в объект Doctor. свойство title до этого, оно было бы утрачено. Вместо этого в нашем скрипте мы добавляем свойство title после того, как Doctor. наследует объекту Person, и проблема решена.
В конце нашего скрипта остались три действия , которые прежде (на шаге 3) вернули такие результаты:
trace(frankenstein.almaMater);// вернуло "Transylvania" trace(frankenstein.title); );// вернуло "Dr." trace(frankenstein.legs);// вернуло undefined
Давайте проверим результаты теперь.
Откроется окно Выход (Output), и в нем отобразится следующее:
Transylvania Dr. 2
Как видите, frankenstein имеет теперь две ноги (тогда как прежде это свойство было не определено – ). Это результат того, что экземпляры Doctor теперь наследуют, как мы выяснили на предыдущем шаге, свойства не только Doctor., но и Person.. По той же причине frankenstein имеет теперь голову – head, а в голове – , и так далее. Если теперь мы изменим любое свойство Person., эти изменения отразятся не только на экземплярах класса Person, но также и на экземплярах класса Doctor, ведь он – Person. Дело, по существу, обстоит так. Когда legs экземпляра frankenstein (на уровне экземпляра), он такого свойства не находит. Тогда он заглядывает в объект Doctor. (поскольку frankenstein есть экземпляр класса Doctor
). Такого свойства нет и там. Наконец Person. (поскольку Doctor есть Person ) – и здесь находит-таки свойство legs.
Теперь вы, возможно, заинтересовались такими свойствами класса Person, как name и age. Эти свойства не входят в объект Person., так будут ли экземпляры класса Doctor наследовать их? Нет, не будут. Чтобы исправить это, нам нужно внести изменения в описание класса Doctor.

Doctor следующим образом:_global.Doctor = function(name, age, almaMater){
this.base = new Person(name, age);
this.name = this.base.name;
this.age = this.base.age;
delete this.base;
this.almaMater = almaMater;
}
Первое, что вы замечаете в обновленном описании класса Doctor – то, что появились два новых аргумента (вдобавок к almaMater, который был и прежде): name и age. Все три значения используются в последующих строках, обеспечивающих создание экземпляров класса Doctor.
В первой строке используются значения name и age, переданные конструктору функции, для создания нового экземпляра класса Person. Да-да, конструктор функции Doctor создает экземпляр класса Person. Как видите, этот экземпляр имеет имя и путь this.base. Видя такую строку, this.base получает свойства name и age (как все экземпляры класса Person ). Следующие две строки конструктора функции задают значения свойств name и age для будущих экземпляров класса Doctor ( this.name и this.age ), причем значения берутся у this.base (экземпляр класса Person, созданный в первой строке). Четвертая строка конструктора функции удаляет экземпляр this.base – он был нужен лишь временно. Пятая строка задает свойство almaMater для будущих экземпляров
(это уже было на шаге 1).
Примечание Это показывает, сколь динамичным может быть при необходимости конструктор функции: функция может создать временный объект (как было показано), выполнить математические вычисления, другие действия, и много чего еще.
Вы, вероятно, думаете, мол, к чему такие сложности, разве нельзя было выполнить описание класса вот так:
_global.Doctor = function(name, age, almaMater){
this.name = name;
this.age = age;
this.almaMater = almaMater;
}
Верно, так было бы проще, и в большинстве случаев подошло бы, но представьте себе, что в описании класса Person с переданными ему значениями name и age производятся какие-либо действия – скажем, все буквы name делаются заглавными, или что-то в этом роде? Тогда в последнем варианте скрипта экземпляры класса Doctor оказались бы лишены этого, тогда как в первоначальном такого не случится.
frankenstein = new Doctor("Frankenstein", 85, "Transylvania");
Необходимость такого изменения вызвана тем, что мы добавили в конструктор функции Doctor параметры age и name. В результате экземпляр frankenstein при создании получит свойства name со значением "Frankenstein", age со значением 85 и almaMater со значением "Transylvania".
trace(frankenstein.name); trace(frankenstein.age);
Эти действия позволят нам увидеть результаты сделанных изменений. Всего в конце скрипта у нас получилось пять действий (три должны остаться с шага 2).
Откроется окно Выход (Output), и в нем отобразится следующее:
Transylvania Dr. 2 Frankenstein 85
Экземпляр frankenstein полностью готов!
Теперь пришло время представить для рассмотрения свойство _proto_. Каждый объект-прототип класса и каждый экземпляр класса имеют свойство _proto_. Его значение показывает, где следует искать свойства, не обнаруженные у самого экземпляра. Как мы знаем, для экземпляра frankenstein в таком случае поиск будет продолжаться в объекте Doctor.. Таким образом, frankenstein._proto_ имеет значение Doctor. (экземпляр наследует этому объекту-прототипу). Поскольку класс Doctor наследует классу Person, Doctor. равно Person. (один прототип класса наследует другому объекту-прототипу класса). Это свойство _proto_ создается и получает значение автоматически при создании экземпляра или Lawyer и хотим связать frankenstein
с этим классом (вместо класса Doctor ). Тогда мы просто используем такую строку скрипта:
frankenstein._proto_ = Lawyer.prototype;
После этого frankenstein можно считать экземпляром класса Lawyer, наследующим характеристики прототипа этого класса. При этом его "докторские" характеристики (унаследованные от объекта Doctor. ) исчезнут начисто!
Осталось рассказать еще о двух важных аспектах Person и Doctor ), то обнаружим следующее:
Person имеет name: это состав.Person имеет head: это состав.Person имеет legs: это состав.Doctor есть Person: это наследование.Doctor имеет title: это состав.Эта концепция имеет место и в реальном мире. Вот, например:
Рассуждая таким образом, вам будет легко проектировать новые
Наследование также имеет некоторые свои аспекты, но это уже выходит за пределы тематики данной книги. Теперь, когда вы понимаете, как создаются любые
В следующем упражнении мы продолжим работу с этим файлом.
До сих пор объекты, которые мы создавали, имели свойства, значения которых мы могли использовать для вычислений и тому подобного, однако они не могли сами выполнять каких-либо задач. Сейчас вы поймете, что это означает – просто вспомните, что умеют делать
play (), остановить – stop(), загрузить фильм – loadMovie(), получить URL – getURL() и так далее.loadSound(), установить громкость – setVolume(), установить баланс – setPan() и тому подобное.А наши классы Person и Doctor ничего делать не могут, кроме как просто "присутствовать". В этом разделе мы постараемся изменить такое положение дел, создав для наших классов методы – это даст возможность экземплярам этих классов выполнять полезные задачи.
Прежде всего следует вам сказать, что метод – это не что иное, как функция, связанная с классом и помещенная в объект-прототип этого класса. Давайте начнем упражнение, и вы увидите, как это работает.
Person.prototype.talk = function(){
trace("Привет, меня зовут "+ this.name +". Мне "+ this.age +" лет.");
}
Примечание Можно создать метод другим способом:
//Сначала описываем функцию
function futureMethod(){
trace("Привет, меня зовут "+ this.name +". Мне "+ this.age +" лет.");
}
//Назначаем эту функцию методом talk класса Person
Person.prototype.talk = futureMethod;

Вот и создан новый метод для Person. Функция сделалась методом класса Person, поскольку мы поместили ее в объект-прототип класса Person.
Этот наш метод, который называется talk(), может быть использован любым экземпляром данного класса. У нас имеются экземпляры person1 и person2. Любой из этих экземпляров может использовать наш новый метод, вот таким образом:
person2.talk();
Этот метод выполняет лишь одну простую задачу: выводит сообщение в окне Выход при тестировании фильма. Как вы видели, в описании метода, в действии , использованы элементы this.name и this.age. Поскольку функция является методом, this есть ссылка на экземпляр, который вызвал этот метод. Таким образом, если этот метод вызывает person2, эти элементы будут означать person2.name и person2.age.
Примечание Если термин this используется в обычной функции, то он означает ссылку на монтажный стол, на котором находится функция.
person2.talk();
Это приведет к тому, что person2 вызовет метод talk().
Откроется окно Выход (Output), и в нем отобразится следующее:
Привет, меня зовут Derek. Мне 29 лет.
Итак, наш метод talk() дал возможность экземплярам нашего класса Person хоть что-то делать. Давайте немного расширим возможности этого метода.
Person.prototype.talk = function(tone){
var message = "Привет, меня зовут "+ this.name +". Мне "+ this.age +" лет.";
if (tone == "крик"){
trace(message.toUpperCase());
}else{
trace(message);
}
}
Как видите, теперь метод принимает параметр . Метод будет действовать одним из двух способов в зависимости от значения параметра. Давайте рассмотрим подробнее обновленное описание метода.
Первым делом создается message, которая получает значение, полученное из выражения. Выражение то же самое, которое мы прежде использовали в действии .
Примечание Как упоминалось в предыдущем уроке (о функциях), message локальной, она была бы доступна любому скрипту в любое время. Будучи локальной, она будет удалена, как только завершится выполнение метода.
После того, как переменная message получила значение, в действие вступает оператор if. Он проверяет значение : если оно равно "крик", то значение message преобразовывается – все буквы делаются заглавными – а затем выводится в окне Выход. Если имеет любое другое значение, message будет выведено обычным образом. Использование метода toUpperCase() объекта String демонстрирует, что метод одного класса может использовать методы других классов. Методы могут содержать действия, другие методы и так далее – практически все, что предоставляет
person2.talk("крик");
В результате person2 вызовет метод talk(), передав ему в качестве параметра значение "крик". Теперь обе последние строки вызывают метод talk(), но разными способами (в одном случае передается значение "крик"); окно Выход покажет нам оба результата.
Откроется окно Выход (Output), и в нем отобразится следующее:
Привет, меня зовут Derek. Мне 29 лет. ПРИВЕТ, МЕНЯ ЗОВУТ DEREK. МНЕ 29 ЛЕТ.
Теперь давайте рассмотрим еще один интересный аспект нашего метода talk().
frankenstein.talk();
frankenstein.talk("крик");
Метод talk() принадлежит классу Person, однако, поскольку класс Doctor есть Person, экземпляры класса Doctor тоже имеют доступ к этому методу – как вы видите на примере экземпляра frankenstein.
Класс Doctor тоже может иметь свои, уникальные, методы – достаточно просто поместить нужные функции в объект Doctor., как мы сделали с объектом Person. на шаге 1.

Примечание Если создать другой метод talk() – в объекте Doctor., то экземпляр frankenstein будет использовать именно этот метод, а не тот, что находится в Person.. Ведь frankenstein – экземпляр класса Doctor, и поиск его методов происходит в первую очередь в объекте Doctor..
Откроется окно Выход (Output), и в нем отобразится следующее:
Привет, меня зовут Frankenstein. Мне 85 лет. ПРИВЕТ, МЕНЯ ЗОВУТ FRANKENSTEIN. МНЕ 85 ЛЕТ.
Наш метод talk() выполняет простейшую задачу, вообще же методы могут быть весьма сложными.
Полученные знания о механике объектов пригодятся вам в следующих разделах, когда мы будем изучать способы расширения функциональности
Итак, вы знаете, что объектам (в том числе фильмам-символам) можно придать свойства (переменные). В типичных watch() для отслеживания различных свойств (переменных, как условия в нашем примере) объекта; если обнаружено изменение значения свойства, то вызывается некая функция, и ваш проект реагирует на изменение. Синтаксис применения этого метода таков:
имяОбъекта.watch("имяСвойства", имяФункции);
Предположим, weather (погода) – имя фильма-символа на основном монтажном столе, (условия) – . Тогда мы запишем так:
_root.weather.watch("condition", watchCondition);
Примечание Объект может инициировать отслеживание внутри себя, используя синтаксис this.watch("имяСвойства", имяФункции);.
Функция watchCondition() могла бы быть описана, например, так:
function watchCondition(id, oldval, newval){
message.text = id + " было " + oldval + ", а теперь " + newval;
if (newval == "rainy"){
sky.gotoAndStop("dark");
lights.gotoAndStop("on");
wipers.gotoAndPlay("on");
}else if (newval == "sunny"){
sky.gotoAndStop("bright");
lights.gotoAndStop("off");
wipers.gotoAndStop("off");
}
}
Обратите внимание на параметры id, oldval и newval, передаваемые функции. Эти параметры будут автоматически передаваться функции при ее вызове в результате срабатывания метода watch(). Параметр id представляет собой имя (в виде строкового значения) отслеживаемого свойства; oldval – старое значение этого свойства (до изменения), а newval – новое значение, которое привело к срабатыванию метода watch(). В приведенном примере вы видите, как эти параметры используются внутри функции.

Примечание Хотя в примере этого не показано, если бы в функции встречалась адресная ссылка this, она означала ссылку на объект, свойство которого отслеживается.
Примечание Необходимо описывать функцию, вызываемую методом watch(), до того, как будет инициировано отслеживание. Ведь в качестве параметра метода watch() вы должны указать функцию, которую следует вызвать при изменении свойства, а указать функцию, которая еще не существует, нельзя.
Если необходимость в отслеживании свойства отпала, вы можете отменить его, вызвав метод unwatch():
weather.unwatch("condition");
После выполнения данной строки скрипта при изменении свойства функция watchCondition() более не будет вызываться.
Несколько важных моментов, касающихся применения метода watch():
watch() работает с большинством объектов _x, _y, _alpha и тому подобные.В следующем упражнении мы создадим сцену, в которой изменение отслеживаемого свойства будет влиять на различные элементы фильма.
Большинство элементов этой сцены мы для вас уже создали и настроили, так что вы можете сосредоточиться на message. В слой Buttons помещены все используемые в нашем проекте кнопки. Четыре из них, которые мы будем использовать в этом упражнении, расположены справа от экземпляра bigHead. Имена кнопок – mouthButton, noseButton, eyesButton и glassesButton. Две другие кнопки, те, что в левом верхнем углу сцены, мы используем в следующем упражнении. Слой Actions содержит скрипты (к которым мы многое добавим по ходу упражнения).

Главное, о чем следует вас предупредить – это то, что монтажный стол этого фильма-символа (сам по себе простенький) содержит экземпляры четырех других фильмов-символов: glassesClip, eyesClip, noseClip и mouthClip. Что они изображают, можно догадаться по названиям – очки, глаза, нос и рот. Мы будем делать эти экземпляры видимыми и невидимыми, отслеживая изменение значений свойств. В нашем фильме два экземпляра фильма-символа Head, и мы запрограммируем их так, чтобы они реагировали на события противоположным образом. Например, если экземпляр noseClip в экземпляре bigHead становится видимым, то noseClip в экземпляре smallHead делается невидимым.
onClipEvent(load){
glassesClip._visible = false;
eyesClip._visible = false;
noseClip._visible = false;
mouthClip._visible = false;
glasses = false;
eyes = false;
nose = false;
mouth = false;
}
Здесь использовано событие load, то есть скрипт будет выполнен, как только экземпляр bigHead появится на сцене – а это произойдет сразу после начала воспроизведения фильма. Кстати, большинство скриптов для этого упражнения мы будем помещать в обработчики события load. А пока давайте рассмотрим эти первые несколько строк.
Четыре первые строки делают невидимыми экземпляры, о которых мы говорили на предыдущем шаге. Таким образом, сразу после загрузки bigHead лишится лица, а smallHead останется без изменений, (то есть, с лицом) – ведь эти действия затрагивают только экземпляры, находящиеся в bigHead.
Четыре следующие строки присваивают значения переменным. Имена переменных (и значения) совпадают с текущим состоянием экземпляров фильмов-символов, о которых только что шла речь. Эти четыре переменные (свойства) мы будем отслеживать на предмет изменений. Сейчас мы присвоили им начальные значения.
Теперь нам нужно описать функции, которые будут вызываться при изменении одного из этих свойств.
function watchGlasses(id, oldval, newval){
_root.message.text = "I now have " + id + ".";
glassesClip._visible = newval;
_root.smallHead.glassesClip._visible = oldval;
_root.glassesButton.enabled = false;
}

Вообще-то мы собираемся установить наблюдение за переменной , а эта функция будет выполняться при изменении значения. Обратите внимание на три параметра, передаваемых функции – id, oldval, newval. Значения всех трех параметров используются в функции. Первая строка функции, используя значение id, будет выводить сообщение в текстовом поле message. Поскольку эту функцию будет вызывать "наблюдатель" за значением , id в данном случае будет иметь строковое значение "newval. На предыдущем шаге мы дали очкам начальное значение false ; это значит, что функция будет вызвана, когда это значение сменится на true, а значит, и newval будет true – это ведь не что иное, как новое значение свойства или переменной. Итак, во второй строке glassesClip._visible становится true, и glassesClip из bigHead отображается на экране. В следующей строке нашел применение параметр oldval – в соответствии с ним устанавливается видимость glassesClip из экземпляра smallHead. А поскольку oldval имеет значение false (значение glassesClip из smallHead становится невидимым. Последнее действие отключает кнопку glassesButton. Функции этой кнопки мы пока не запрограммировали, но заранее скажем, что именно она будет изменять значение , что и приведет к выполнению данной функции. Как видите, изменив это значение, кнопка отключается, то есть, использовать ее можно будет лишь один раз.
function watchEyes(id, oldval, newval){
_root.message.text = "I now have " + id + ".";
eyesClip._visible = newval;
_root.smallHead.eyesClip._visible = oldval;
_root.eyesButton.enabled = false;
}
function watchNose(id, oldval, newval){
_root.message.text = "I now have " + id + ".";
noseClip._visible = newval;
_root.smallHead.noseClip._visible = oldval;
_root.noseButton.enabled = false;
}
function watchMouth(id, oldval, newval){
_root.message.text = "I now have " + id + ".";
mouthClip._visible = newval;
_root.smallHead.mouthClip._visible = oldval;
_root.mouthButton.enabled = false;
}
Эти три функции будут обрабатывать изменение переменных eyes, nose и mouth, соответственно. На шаге мы задали для всех этих переменных начальное значение false ; значит, соответствующие функции будут выполняться при изменении значения на true. Работают эти функции точно так же, как та, которую мы создали на предыдущем шаге. Различия заключаются лишь в именах самих функций, в именах экземпляров, которые становятся видимыми (в bigHead ) и невидимыми ( smallHead ), и именах отключаемых кнопок.
Итак, функции созданы, пора заняться и "наблюдателями".
Примечание Помните, что функции, используемые методом watch(), должны быть описаны до того, как будет установлен сам "наблюдатель".
this.watch("glasses", watchGlasses);
this.watch("eyes", watchEyes);
this.watch("nose", watchNose);
this.watch("mouth", watchMouth);
Эти четыре строки содержат указания . При изменении значения этого свойства выполняется функция watchGlasses(). Обратите внимание на использование здесь термина this. Первая строка, в сущности, гласит: "Отслеживать изменение значения свойства в "этом" ( this ) объекте". Если бы переменная находилась в другом экземпляре, мы могли бы попросту использовать адресацию – указать путь к этому экземпляру (и/или функции), к примеру, вот так:
_root.myMovieClip.watch("glasses", _root.myFunction);
Остальные три строки скрипта устанавливают наблюдение за свойствами eyes, nose и mouth, и при их изменении выполняют соответствующие функции.
Нам осталось лишь запрограммировать четыре кнопки, которыми мы и будем изменять значения четырех свойств.
on(release){
_root.bigHead.mouth = true;
}

Как только эта кнопка будет нажата и отпущена, значение свойства mouth экземпляра bigHead изменится с false на true, и на это отреагирует "наблюдатель", установленный нами на предыдущем шаге.
К кнопке noseButton:
on(release){
_root.bigHead.nose = true;
}
К кнопке eyesButton:
on(release){
_root.bigHead.eyes = true;
}
К кнопке glassesButton:
on(release){
_root.bigHead.glasses = true;
}
Все то же самое, что и с предыдущей кнопкой. Различаются только свойства, значения которых изменяются при нажатии каждой из кнопок.
Итак, изначально bigHead у нас совершенно "обезличен". Нажатие той или иной кнопки справа изменит значение mouth, nose, eyes либо , что приведет к выполнению одной из наших функций. В сцене произойдут изменения – как мы и запрограммировали.
На этом упражнение закончено. Мы увидели как используется отслеживание свойств – единственное изменение значения может запустить нужную функцию. Конечно, в этом упражнении можно было обойтись без всякого отслеживания – попросту вызывать нужные функции кнопками. Вообще же технология отслеживания свойств может быть очень и очень полезна – особенно в ситуации, когда изменение единственного свойства объекта имеет решающее значение для общей функциональности проекта. В таких случаях бывает проще сразу установить наблюдение за нужным свойством, чем потом то и дело проверять его значение, чтобы вызвать функцию. Да и вообще со свойствами как-то удобнее работать.
Встроенные во
Усовершенствовать существующие методы можно двумя способами – в зависимости от типа объекта. Для некоторых объектов нельзя создать экземпляры – они единственны и универсальны для каждого проекта: это объекты и Math. К примеру, вы не можете создать экземпляр объекта – мышь у компьютера только одна, и на экране только один указатель мыши; вы можете лишь использовать методы объекта и т.п.). Итак, если экземпляр объекта создать нельзя, то для усовершенствования метода этого объекта используется такой способ:
MathPlus = new Object();
MathPlus._proto_ = Math;
MathPlus.oldMax = Math.max;
MathPlus.max = function(x, y, z){
firstComparison = MathPlus.oldMax(x, y);
secondComparison = MathPlus.oldMax(firstComparison, z);
return secondComparison;
}
Object.prototype.Math = MathPlus;
trace(Math.max(23, 56, 4));
Данный скрипт усовершенствует метод max() объекта Math, который в нормальном своем виде находит большее из двух чисел. Например, в этом примере myVariable получит значение 456:
myVariable = Math.max(456, 23);
Приведенный выше скрипт изменяет этот метод так, чтобы он находил большее из трех чисел. Вот как это делается: в первой строке создается родовой объект с именем MathPlus. В следующей строке используется свойство _proto_, чтобы экземпляр этого нового объекта унаследовал все функциональные возможности объекта Math.
Примечание Здесь мы не можем обеспечить наследование, используя выражение MathPlus = new Math() ; (как мы делали прежде с экземплярами Doctor и Person ), поскольку экземпляр объекта Math создать нельзя – выражение new Math() не имеет смысла. Поэтому, чтобы объект MathPlus унаследовал возможности объекта Math, мы должны использовать свойство _proto_, как и показано в скрипте – результат, в принципе, тот же самый, только достигается иным путем (об этом свойстве мы рассказывали в этом уроке, в разделе "Создание
Теперь MathPlus "умеет" делать все, на что способен объект Math. Например, Math. ; и MathPlus. ; дадут один и тот же результат. Почему мы решили пойти таким путем? Дело в том, что напрямую манипулировать методами объекта Math (как и других объектов, не позволяющих создавать экземпляры) нельзя. Поэтому мы создаем объект-посредник ( MathPlus ), который наследует возможности объекта Math, изменяем функции этого посредника, а затем (такое вот извращение) велим MathPlus ) объектом Math.
Итак, в двух следующих строках скрипта описываются два новых метода объекта MathPlus: oldMax, который копирует оригинальный метод Math.max(), и max, который станет новым, усовершенствованным методом max(). Метод oldMax создать необходимо, поскольку функции оригинального метода max() все же понадобятся новому методу max(). Однако мы должны дать оригинальному методу новое имя, так как имя max будет теперь принадлежать новому, измененному методу. Функция, описывающая новый метод max(), принимает три параметра ( x, y, z ). Значения этих трех MathPlus методом Math прототипа класса Object (это значит, что объект MathPlus заместит прежний объект Math ).

Можем заодно усовершенствовать метод объекта Math, чтобы он генерировал случайное число из заданного диапазона. Это можно сделать с помощью таких строк (предполагается, что MathPlus уже создан и "получил наследство", как показано в первых двух строках предыдущего примера):
MathPlus.oldRand = Math.random;
MathPlus.random = function(min, max){
return MathPlus.floor(MathPlus.oldRand() * ((max - min) + 1)) + min;
}
Как видите, синтаксически это похоже на предыдущий пример. С помощью подобных синтаксических структур можно усовершенствовать и методы объектов .
Способ изменения методов таких классов, которые позволяют создавать экземпляры (например, и т.п.), отличается – но не намного. В этом упражнении мы усовершенствуем метод duplicateMovieClip(). Используя существующий метод, мы имеем возможность указать, какой фильм-символ следует дублировать, какое имя дать дубликату и на какую глубину следует поместить дубликат (подробнее об этом методе см. в Уроке 14 – Динамическое управление фильмами-символами). А мы усовершенствуем этот метод, добавив возможность перемещать оригинальный фильм-символ (тот, который дублируется) в указанные xy координаты по окончании процесса дублирования.
Мы уже работали с этим проектом в предыдущем упражнении, так что его элементы должны быть вам знакомы. В этом упражнении мы присоединим скрипты к кадру 1, а также к кнопкам (в виде сдвоенных квадратов), расположенным в левом верхнем углу сцены.
MovieClip.prototype.oldDuplicateMovieClip =
MovieClip.prototype.duplicateMovieClip;
MovieClip.prototype.duplicateMovieClip = function(name, depth, moveX, moveY){
this.oldDuplicateMovieClip(name, depth);
this._x = moveX;
this._y = moveY;
}
Мы поместили этот скрипт в кадр 1, значит, как только начнется воспроизведение фильма, метод duplicateMovieClip() будет изменен, после чего, в новом виде, станет доступен всем экземплярам фильмов-символов.
Методами объекта Math мы напрямую манипулировать не могли (пришлось использовать объект-посредник), а вот методами класса – вполне можем, поскольку эти методы находятся в объекте , который нам ничего не стоит изменить.
Первая строка скрипта копирует оригинальный метод duplicateMovieClip() в oldDuplicateMovieClip(). Это мы делаем для того, чтобы сохранить его функциональность, она нам пригодится, когда мы будем описывать работу усовершенствованного метода. Со следующей строки начинается функция, описывающая новый, измененный метод duplicateMovieClip(). Этот метод будет принимать два дополнительных параметра: moveX и moveY (сейчас мы поясним, как они будут использованы). В первой строке функции используется метод oldDuplicateMovieClip() – он выполняет то, что раньше делал оригинальный метод duplicateMovieClip(), то есть дублирует фильм-символ, дает ему имя и назначает глубину. Примененный здесь термин this ссылается на оригинальный экземпляр фильма-символа (тот, который дублируется). Следующие две строки, используя значения параметров moveX и moveY, перемещают оригинальный экземпляр в соответствующую координатную точку. Все – наш новый метод duplicateMovieClip() готов к использованию экземпляром любого фильма-символа!
on(release){
smallHead.duplicateMovieClip("myHead1", 10, 200, 150);
}

Когда эта кнопка будет нажата и отпущена, произойдет дублирование экземпляра smallHead. Дубликат получит имя myHead1 и глубину 10. Два других параметра – это значения, соответственно, moveX и moveY, они используются для перемещения оригинального экземпляра после дублирования. Таким образом, smallHead будет перемещен в точку с координатами 200 по x и 150 по y.
on(release){
bigHead.duplicateMovieClip("myHead2", 10, 100, 200);
}
Здесь (таким же образом) будет продублирован экземпляр bigHead.
При нажатии на каждую из запрограммированных нами кнопок соответствующий экземпляр будет дублирован и перемещен в указанную точку координат.
В нашем примере изменение существующего метода был простым, однако такие усовершенствования могут быть весьма сложными – процесс может включать очень много действий TextField, и т.д.).
Это упражнение завершено, но в следующем мы продолжим работу над данным проектом.
В предыдущем упражнении вы узнали, как можно усовершенствовать существующий метод
Как и при изменении существующих, тут подход тоже зависит от того, создается ли новый метод для встроенного объекта ( ) или и т.п.).
Чтобы создать новый метод для встроенного объекта (мы опять возьмем для примера объект Math) следует использовать такую синтаксическую конструкцию:
Math.totalWithTax = function(total, taxPercent){
convertedPercent = taxPercent / 100;
totalTax = total * convertedPercent;
return total + totalTax;
}
trace(Math.totalWithTax(24.00, 6)); // Выводит результат: 25.44
Как видите, чтобы добавить метод (в данном случае – totalWithTax() ) к объекту Math, мы попросту присоединили к нему функцию. Теперь этот метод будет доступен в любом месте фильма. Таким же образом присоединяются новые методы и к другим
В следующем упражнении мы покажем, как добавить новый метод к классу , но описанный способ подойдет и для других
С этим проектом мы работали, выполняя два предыдущих упражнения, так что с его элементами вы знакомы. В этом упражнении мы добавим скрипт в кадр 1, а также к кнопкам в виде двусторонних стрелок, что в левой верхней части сцены.
MovieClip.prototype.flip = function(mode){
if (mode.toLowerCase() == "h"){
this._xscale = -this._xscale;
}else if (mode.toLowerCase() == "v"){
this._yscale = -this._yscale;
}
}

Мы поместили этот скрипт в кадр 1, поэтому он будет выполнен сразу после начала воспроизведения фильма, и после этого метод , добавленный нами в объект MoviClip.mode. Его значение может быть либо "h", либо "v" –указание на поворот по горизонтали или вертикали.
В зависимости от значения mode функция решает, как действовать – помогает ей в этом оператор if. В первой секции этого оператора значение mode преобразуется в нижний регистр (строчную букву), после чего сравнивается со значением "h". Если преобразованное значение mode равно "h", значит, нужно переворачивать по горизонтали. Делается это путем изменения знака текущего значения xscale (масштаб по x) данного экземпляра фильма-символа ( this ). То есть, если это значение равно 75, то будет –75; если оно равно –34, то будет 34. Обращение значения выполняется при помощи оператора "минус" (-). При таком использовании этот оператор превращает положительное число в отрицательное и наоборот. Таким вот образом масштабирование фильма-символа приспособлено для переворачивания.
Секция else вступает в дело, если mode имеет значение "v". В этом случае осуществляется вертикальный переворот – меняется на знак свойства yscale.
Примечание Преобразование значения mode в символ нижнего регистра мы предусмотрели на случай нечаянного ввода "H" или "V" вместо "h" и "v" – ведь "H" не равно "h". Такое преобразование помогает снизить вероятность ошибки при использовании нового метода.
on(release){
smallHead.flip("v");
}
При нажатии этой кнопки экземпляр smallHead будет переворачиваться по вертикали.
on(release){
smallHead.flip("h");
}
Это будет переворачивать экземпляр smallHead по горизонтали.
При нажатии на какую-либо из этих кнопок тот или иной экземпляр фильма-символа будет переворачиваться по горизонтали или вертикали.

Это был простой пример того, как можно создать свой метод для TextField, и т.п.).
Это упражнение завершено, а в следующем мы продолжим работу со слегка модифицированной версией этого проекта.
Совет Отличный сетевой ресурс, содержащий новые и усовершенствованные методы
Совет Вы можете создать собственную библиотеку дополнительных методов (а также и классов), написав нужный код в стандартном текстовом редакторе и сохранив файл с расширением .as – что-нибудь вроде CustomScript.as – файлы с таким расширением #include, указав имя файла, который следует включить в программу (например, #include "CustomScript.as" ). Это действие автоматически импортирует и вставляет код из .as файла в то место программы, где находится действие #include. Таких .as файлов, содержащих код для повторного использования, вы можете создать сколько угодно.
Одна из наиболее ценных новинок clipOne и clipTwo.
Примечание Обратите внимание, это важно: далее в тексте, говоря о фильме-символе, мы будем иметь в виду образец, хранящийся в библиотеке. Экземпляр фильма-символа или просто экземпляр, является копией этого образца.
Итак, давайте создадим новый класс:
_global.CustomClass = function(){
}
Мы создали новый класс под именем CustomClass. Далее мы укажем, что этот класс должен наследовать возможности класса – для этого сразу после описания класса мы поместим такую строку:
CustomClass.prototype = new MovieClip();
Теперь придадим нашему классу дополнительные возможности, добавив их в объект-прототип:
CustomClass.prototype.onRollOver = function(){
this._alpha = 50;
}
CustomClass.prototype.onRollOut = function(){
this._alpha = 100;
}
CustomClass.prototype.onEnterFrame = function(){
this._rotation += 10;
}
Наш класс получил, посредством трех методов – обработчиков событий, три функции интерактивного взаимодействия. Теперь нужно, чтобы экземпляры фильма-символа clipOne могли наследовать эти возможности. Для этого нам следует воспользоваться методом Object.registerClass():
Object.registerClass("clipOne", CustomClass);
Теперь все экземпляры фильма-символа clipOne будут реагировать на , rollOut и enterFrame так, как предписывает CustomClass. Технически они более не считаются экземплярами – вместо этого они стали экземплярами CustomClass.
Примечание При описании методов – обработчиков событий использовался термин this ; в итоге он будет адресной ссылкой на каждый конкретный экземпляр, унаследовавший функции.
Если вдруг необходимость в наследовании экземплярами clipOne функциональности класса CustomClass отпадет, можно применить следующий синтаксис:
Object.registerClass("clipOne", null);
Регистрация фильма-символа как класса и ее отмена не имеет эффекта для существующих экземпляров данного фильма-символа, она действует только для новых экземпляров, помещаемых в фильм – прямо на монтажный стол в процессе разработки либо динамически, посредством duplicateMovieClip() или . Существующие экземпляры сохраняют текущую функциональность. Чтобы вы лучше поняли этот принцип, давайте рассмотрим пример. Допустим, в кадре 1 вашего фильма вы описали CustomClass. В этот же кадр вы поместили экземпляр фильма-символа clipOne и назвали его myInstance. В кадр 2 вы поместили действие, регистрирующее clipOne как CustomClass:
Object.registerClass("clipOne", CustomClass);
Теперь, хотя мы зарегистрировали clipOne как CustomClass, экземпляр этого фильма-символа myInstance (помещенный в кадр 1) не унаследует функциональность CustomClass, поскольку этот экземпляр появился в фильме (в кадре 1) до того, как был вызван метод registerClass() (в кадре 2). Только те экземпляры, которые будут помещены в фильм с этого момента – вручную или динамически – будут наследовать возможности CustomClass.

Но что же делать, если необходимо поместить экземпляры clipOne в кадр 1, притом так, чтобы они унаследовали возможности CustomClass, описанного в этом же кадре? Об этом вы узнаете, выполнив упражнение этого раздела. Важно уяснить, как это работает, в противном случае даже долгие часы мучений ни к чему не приведут.
Фильм-символ (как тот же clipOne ) может быть связан (зарегистрирован) только с одним классом в каждый момент времени. Если вы, зарегистрировав фильм как класс, затем регистрируете его как другой класс, то эта последняя регистрация заменит предыдущую.
Однако это не означает, что несколько фильмов нельзя связать с одним классом – это как раз можно. Просто вызываете опять метод registerClass() – столько раз, сколько нужно:
Object.registerClass("clipTwo", CustomClass);
Object.registerClass("myClip", CustomClass);
Object.registerClass("anotherClip", CustomClass);
В результате этих действий экземпляры фильмов-символов clipTwo, myClip и anotherClip будут наследовать возможности CustomClass.
Совет Если вы хотите ассоциировать со своим классом только отдельный экземпляр фильма-символа, просто назначьте свойству _proto_ объект-прототип этого класса:
myMovieClip._proto_ = CustomClass.prototype;
В этом случае экземпляр myMovieClip унаследует возможности CustomClass. Такое изменение свойства _proto_ отражается на экземпляре немедленно.
Итак, в следующем упражнении мы создадим собственный класс, настроим его функциональные возможности, а затем, с помощью метода registerClass(), назначим фильм-символ из библиотеки объектом нашего класса.
С этим проектом мы работали в предыдущих упражнениях, его элементы вам знакомы. Однако здесь мы несколько изменили структуру проекта. Теперь проект содержит две сцены, идентичные во всем, кроме одного: сцена 1 содержит кнопку, при нажатии которой происходит переход к сцене 2, а во второй сцене, наоборот, имеется кнопка для перехода в сцену 1. Кроме того, в кадр 1 сцены 1 добавлено действие stop(), чтобы пользователь мог переключаться между сценами с помощью кнопок.
Кнопки справа от экземпляра bigHead теперь заменены четырьмя экземплярами фильма-символа . В среде разработки эти экземпляры выглядят одинаково (изображения рта), однако на самом деле фильм-символ содержит все изображения, которые мы использовали ранее для кнопок (глаза, нос, очки). К тому времени, как мы закончим программировать этот проект и проиграем его, эти четыре экземпляра будут выглядеть так же, как те кнопки в предыдущих упражнениях.
В этом упражнении мы сделаем так, чтобы фильм-символ – а значит, и эти четыре экземпляра – унаследовали функции нового класса, который мы создадим. В результате эти четыре экземпляра станут как бы усовершенствованными версиями тех кнопок, которые мы использовали прежде.
Четыре экземпляра имеют следующие имена (сверху вниз): mouth, nose, eyes и . Запомните эти имена: в построении проекта они будут важны. Скрипты в этом упражнении мы будем помещать в основном на монтажный стол фильма-символа . Прежде, чем заняться этим, давайте откроем библиотеку и посмотрим, что представляет собою этот фильм-символ.
В открывшемся диалоговом окне Свойства связи (Linkage Properties) вы видите, что фильм символ имеет идентификатор (имя) "

Примечание Подробнее о настройках связи смотри в Уроке 14 – Динамическое управление фильмами-символами.
Монтажный стол этого клипа содержит четыре слоя, в каждом из которых – 40 кадров. В слое Labels имеется четыре mouth, nose, eyes и . Каждая из них обозначает иное графическое изображение (в слое Graphics ), соответствующее названию метки. Слой
_global.Custom1 = function(){
}
Custom1.prototype = new MovieClip();
Первые две строки создают новый класс под названием Custom1. Как видите, описание класса предельно простое. Следующая строка устанавливает наследование, в результате которого класс Custom1 получит все функциональные возможности класса . Теперь мы опишем особые возможности, присущие классу Custom1.
Custom1.prototype.scale = function(amount){
this._xscale = amount;
this._yscale = amount;
}
Этот блок кода описывает для класса Custom1 новый метод под названием . Метод будет управлять масштабированием экземпляра, основываясь на значении , передаваемом методу при вызове. Скоро мы им воспользуемся.
Примечание В этом скрипте (и в последующих) this в итоге станет ссылкой на тот экземпляр, который унаследует возможности класса. Так, если экземпляр с именем myClip станет наследником Custom1, действие myClip. ; установит масштаб в 50 процентов для этого экземпляра.
Custom1.prototype.onLoad = function(){
this.gotoAndStop(this._name);
}
В этой части скрипта мы создаем метод - обработчик события onLoad – он описывает, что должно происходить при загрузке экземпляров данного класса. Действие gotoAndStop() будет перемещать каждый экземпляр к метке, соответствующей его имени. Четыре экземпляра фильма-символа унаследуют эту способность класса. Имена этих экземпляров – mouth, nose, eyes и – такие же, как имена четырех меток на монтажном столе данного фильма-символа. Таким образом, при загрузке (onLoad) экземпляр перейдет к метке, соответствующей его имени ( this._name ). В результате после начала фильма четыре экземпляра будут выглядеть так же, как прежние четыре кнопки.

Custom1.prototype.onPress = function(){
thisX = this._x;
thisY = this._y;
startDrag(this, true);
this.scale(70);
}
В этой части скрипта использован метод-обработчик события onPress, он устанавливает, что должно происходить с экземплярами данного класса при нажатии на них кнопки мыши. Первые две строки фиксируют текущие координаты экземпляра по x и y и помещают значения в переменные с именами thisX и thisY. Следующее действие делает экземпляр перетаскиваемым. В последней строке используется метод , созданный нами на предыдущем шаге – в данном случае он уменьшает экземпляр до 70 процентов от нормального размера.
Custom1.prototype.onRelease = function(){
stopDrag();
if (this.hitTest("_root.bigHead")){
this._x = thisX;
this._y = thisY;
_root.bigHead[this._name] = true;
this.scale(100);
this.enabled = false;
}else{
this._x = thisX;
this._y = thisY;
this.scale(100);
}
}
В этой части скрипта применен метод-обработчик события onRelease, описывающий, что должно происходить с экземплярами данного класса при отпускании нажатой кнопки мыши. Первым делом прекращается перетаскивание экземпляра. Затем вступает в действие оператор if, выполняющий один из двух наборов инструкций в зависимости от того, где оказался экземпляр в результате перетаскивания. Для проверки условия использован метод hitTest() – с его помощью выясняется, оказался ли экземпляр после перетаскивания на экземпляре bigHead. Если это так, то выполняется первый набор действий. Два первых действия перемещают экземпляр в координаты со значениями thisX и thisY – то есть в изначальное положение. В следующем действии свойству экземпляра bigHead, имеющему такое же название, как имя текущего экземпляра, присваивается значение true. Например, если нажатый и отпущенный экземпляр имеет имя nose, то данная строка расшифруется так:
_root.bigHead.nose = true;
Таким образом, изменяется значение свойства nose экземпляра bigHead – а оно, как вы помните, отслеживается, так что в результате этого изменения станет видимым нос на большом лице (мы запрограммировали это в одном из предыдущих упражнений.
Следующее действие возвращает экземпляру первоначальный размер, а последнее – "отключает" экземпляр, так что он не будет более реагировать на событие onPress (это относится только к данному конкретному экземпляру).
Секция else выполняется, если экземпляр в результате перетаскивания не попал на bigHead. В этом случае выполняется всего три действия. Экземпляр попросту возвращается в начальное положение и принимает нормальный размер. После этого его можно снова нажимать и перетаскивать, и тогда снова оператор if предпримет соответствующие действия.
Программирование функциональных возможностей нашего класса закончено. Осталось ассоциировать с этим классом фильм-символ , чтобы его экземпляры наследовали функции класса Custom1.
Object.registerClass("icons", Custom1);
Мы регистрируем фильм-символ (его Custom1 ; теперь экземпляры фильма будут наследовать все свойства и методы этого класса.
С началом воспроизведения фильма вы обнаружите, что у нас проблемы – проект не функционирует. Четыре экземпляра фильма-символа , похоже, не унаследовали возможности Custom1, как мы планировали. Но если нажать кнопку Go to registerClass() не имел эффекта, а после переключения сцен заработал. Ладно, давайте сперва внесем нужные исправления, а потом разберем, в чем было дело.
#initclip // код, введенный на предыдущих шагах #endinitclip
Эти действия заключают между собой так называемый блок инициализации. Что это такое, мы сейчас разъясним, но прежде давайте проверим, как работает фильм теперь.

На сей раз вы увидите, что наши четыре экземпляра функционируют как должно: работает и перетаскивание, и все остальное; экземпляры обладают всеми возможностями класса Custom1. Если перейти в сцену 2, то и там все будет в порядке. Так в чем же тут дело?
Когда мы тестировали фильм в прошлый раз, эффект регистрации фильма-символа как представителя класса Custom1 явно не имел места, а теперь все сработало. Давайте разберем по пунктам, как развивались события при первом тестировании фильма:
Custom1, и здесь же находится registerClass() ).Icons будут наследовать возможности класса Custom1.Теперь понятно, почему не работали четыре экземпляра – они появились на сцене (третий пункт) до того, как произошла регистрация (пятый пункт). А, как вы помните, регистрация и ее отмены не имеет эффекта для существующих экземпляров фильма-символа – только для новых. Существующие экземпляры сохраняют прежние функциональные возможности. Вот и в нашем фильме к моменту регистрации четыре экземпляра уже были созданы, и потому ничего не унаследовали. Ну, а как же экземпляры во второй сцене? Они-то работали. Верно, работали, поскольку переход в сцену 2 (и создание экземпляров в ней) можно считать седьмым пунктом в нашем перечне событий. Регистрация уже состоялась, и на экземпляры сцены 2 подействовала. Переход обратно в сцену 1 будет восьмым пунктом; экземпляры созданы заново, и регистрация тоже дала нужный результат. Ну вот, мы выяснили, почему так странно (на первый взгляд) работал первоначальный тест. А каким же образом решили проблему два действия, которые мы добавили?
Нам требовался механизм, который обеспечил бы наследование четырьмя экземплярами функций класса Custom1 – то есть выполнил регистрацию – до загрузки кадра 1 и создания этих экземпляров. Так вот, действия # и #endinitclip – и есть такой механизм. Это специальные команды, которые используются только в фильмах-символах (и только в их кадре 1); они позволяют управлять процессом инициализации фильма – иначе говоря, порядком выполнения действий. Вот как это работает.
Помните, в диалоговом окне Свойства связи (Linkage Properties) для фильма-символа (из библиотеки) была установлена опция "Экспорт в первый кадр" (", обнаружил в его первом кадре блок # – #endinitclip и должен был немедленно выполнить все действия заключенные в этом блоке. Таким образом этот скрипт был выполнен до загрузки кадра 1 основного монтажного стола, и порядок событий получился таким:
#initclip – #endinitclip (в этом блоке как раз находится описание класса Custom1, а также registerClass() ).Icons будут наследовать возможности класса Custom1.Icons ).#initclip – #endinitclip ).И в результате процесс регистрации состоялся (второй пункт) до того, как были созданы экземпляры кадра 1 (пятый пункт). Наши четыре экземпляра благополучно унаследовали все, что нужно – как и показало второе тестирование.
Конечно, не обязательно программировать создание класса и регистрацию фильма-символа как объекта этого класса именно тем способом, который был описан в данном упражнении. Главное – чтобы вы понимали порядок выполнения скриптов и то, как этот порядок повлияет на процесс регистрации, а способов можно выбрать в зависимости от обстоятельств.
Примечание Показанную здесь схему инициализации (с блоком # ) используют все компоненты
Примечание Если для клипа, содержащего блок #, не установлена опция "Экспорт в первый кадр" ("
Давайте потренируемся еще на одном примере, добавим в наш проект последний скрипт.

Этот клип изображает у нас на сцене головы. Мы присваиваем ему идентификатор, чтобы можно было его зарегистрировать как объект еще одного нового класса, который мы сейчас создадим.
_global.Custom2 = function(){
}
Custom2.prototype = new MovieClip();
Custom2.prototype.onEnterFrame = function(){
this._rotation += 5;
}
Object.registerClass("Icons", Custom2);
Object.registerClass("headClip", Custom2);
В шести первых строках создается новый класс под названием Custom2 ; его экземплярам дается способность поворачиваться на 5 градусов каждый раз при наступлении события enterFrame. Две последние строки регистрируют фильмы-символы и HeadClip как объекты класса Custom2 – после нажатия кнопки их экземпляры унаследуют возможности этого класса. Заметьте – мы поместили эти действия перед действием nextScene(): это сделано для того, чтобы зарегистрировать клипы до перехода в следующую сцену, в которой создаются экземпляры этих символов. Если бы мы поступили наоборот и поместили этот скрипт после действия nextScene(), то сначала произошел бы переход во вторую сцену, были бы созданы экземпляры, и только потом состоялась бы регистрация клипов. При таком порядке событий регистрация не имела бы эффекта – ведь экземпляры клипов уже существуют.
Итак, при нажатии этой кнопки и выполнении скрипта регистрация клипа как объекта класса Custom2 отменяет предыдущую связь его с классом Custom1 (которую мы создавали и обсуждали в предыдущих упражнениях). Таким образом, изначально, в сцене 1, экземпляры клипа будут функционировать как объекты класса Custom1, но после нажатия этой кнопки все последующие экземпляры будут наследовать функциональность Custom2 (как и экземпляры фильма-символа HeadClip ).
Этот скрипт демонстрирует, что как объявление нового класса, так и регистрация могут происходить в любом месте фильма. Соблюдая правильный порядок событий при регистрации, вы можете осуществлять ее, когда и как вам удобно.
Сразу после начала воспроизведения фильма четыре экземпляра справа от bigHead ведут себя как объекты класса Custom1. Нажмите кнопку перехода в сцену 2, и эти экземпляры, как и экземпляры фильма-символа HeadClip, станут функционировать, как объекты класса Custom2. Их больше нельзя перетаскивать, они просто вращаются, как предписывает класс Custom2.

Уф, даже не верится – этот урок закончился. Долгим он был, зато вы так много узнали об объектах, наследовании, классах, подклассах, методах и о многом другом. Теперь вам по силам взяться за разработку весьма сложных проектов. А кроме того, все эти знания помогут вам в последующих уроках этой книги.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.