Руководство по actionscript. часть 2, стр. 112
В качестве нашего первого приема динамического программирования мы рассмотрим динамические переменные экземпляра — переменные, добавляемые в конкретный объект на этапе выполнения программы.
Динамические переменные экземпляра
В самом начале этой книги написание программы на языке ActionScript сравнивалось с проектированием и разработкой самолета. Чертежи самолета сравнивались с классами ActionScript, а реальные детали конкретного физического самолета—с объектами ActionScript. По этой аналогии структура каждого конкретного самолета будет гарантированно совпадать со структурой всех других самолетов, поскольку все самолеты построены по одному и тому же чертежу. Для сравнения отметим, что все экземпляры конкретного класса будут иметь одинаковую структуру, поскольку в их основе лежит один и тот же класс.
Что же произойдет, если владелец одного из самолетов решит закрепить собственный фонарь на верху своего самолета? Этот самолет будет обладать специфической характеристикой, не присущей всем остальным самолетам. В языке ActionScript «добавление нового фонаря на конкретный самолет» аналогично добавлению новой переменной экземпляра в отдельно взятый конкретный объект, при этом в остальные экземпляры класса данного объекта этот «фонарь» не добавляется. Переменная экземпляра такого рода называется динамической переменной экземпляра. В сравнении с динамическими переменными экземпляра «обычные» переменные экземпляра называются фиксированными.
Динамические переменные экземпляра могут быть добавлены только в экземпляры классов, объявленных с использованием атрибута dynamic (подобные классы называются динамическими). Динамические переменные экземпляра не могут быть добавлены в экземпляры классов, которые не объявлены с использованием атрибута dynamic (подобные классы называются закрытыми). Подкласс динамического класса считается динамическим только в том случае, если его определение тоже включает атрибут dynamic.
Следующий код создает новый класс Person, который может представлять человека в статистической программе, отслеживающей демографическую«ситуацию. Поскольку класс Person объявлен с использованием атрибута dynamic, в любой отдельный объект класса Person на этапе выполнения программы можно добавлять динамические переменные экземпляра.
dynamic public class Person { }
После того как класс будет объявлен динамическим, мы сможем добавлять новые динамические переменные в любой экземпляр этого класса с помощью стандартного оператора присваивания. Например, следующий код добавляет динамическую переменную экземпляра eyeColor в объект класса Person:
var person.-Person = new Person( ); person. eyeColor = «brown»;
Как уже известно из гл. 8, если бы класс Person не был объявлен с использованием атрибута dynamic, приведенный код вызвал бы ошибку обращения, поскольку в классе Person не определена переменная экземпляра с именем eyeColor. Тем не менее в данном случае класс Person объявлен с использованием атрибута dynamic. В результате, когда среда выполнения Flash попытается присвоить значение несуществующей переменной экземпляра eyeColor, вместо того чтобы сообщить об ошибке, она просто создаст динамическую переменную экземпляра с именем eyeColor и присвоит ей указанное значение («brown»). Обратите внимание, что определение динамической переменной экземпляра в предыдущем коде не содержит и не должно содержать объявление типа и модификатор управления доступом.
I Все динамические переменные экземпляра являются нетипизированными и открытыми.
-
Следующий код, в котором происходит попытка использовать объявление типа при создании переменной eyeColor, вызовет ошибку на этапе компиляции:
person. eyeColor:String = «brown»; // Ошибка! Использование :Stri ng здесь
// недопустимо
Для получения значения динамической переменной экземпляра применяется стандартное выражение доступа к переменной, как показано в следующем коде:
trace(person. eyeColor); // Выводит: brown
Если указанная переменная экземпляра (динамическая или нединамическая) не существует, среда выполнения Flash вернет значение undefined, как показано в следующем коде:
trace(person. age); // Выводит undefined, поскольку объект person // не имеет переменной экземпляра с именем age
Наиболее активное использование динамических переменных экземпляра языка ActionScript присуще среде разработки Flash, где каждая анимированная временная шкала представляется подклассом встроенного класса MovieClip. В среде разработки Flash автоматически генерируемые подклассы класса MovieClip являются динамическими, чтобы программисты могли определять новые переменные в создаваемых вручную экземплярах клипов. Подробное описание этой методики, а также описания других приемов работы с временными шкалами можно найти в гл. 29.
Руководство по actionscript. часть 2, стр. 113
Динамические переменные экземпляра иногда используются для создания простой «справочной таблицы», рассматриваемой далее, в разд. «Использование динамических переменных экземпляра для создания справочных таблиц».
Следует помнить, что возможность динамического изменения программы может привести к проблемам, которые очень сложно выявить. Например, если класс объявлен динамическим, чтобы поддержать динамические переменные экземпляра, настоящие ошибки обращения, возникающие при использовании экземпляров данного класса, могут запросто остаться незамеченными (поскольку обращение
к несуществующей переменной экземпляра не вызовет ошибки ни на этапе компиляции, ни на этапе выполнения программы). Единственный способ узнать наверняка, работает ли динамически изменяемая программа, — это запустить ее и понаблюдать за поведением. Подобное наблюдение отнимает много времени и может приводить к ошибкам, вызванным человеческим фактором, поэтому большинство программистов избегают использования динамических переменных в сложных программах.
Среде выполнения Flash для обращения к динамической переменной экземпляра требуется больше времени, чем для обращения к фиксированной переменной. Когда производительность является решающим фактором, избегайте использования динамических переменных экземпляра.
Обработка динамических переменных экземпляра с помощью циклов for-each-in и for-in. Цикл f or-each-in обеспечивает простой способ обработки значений динамических переменных экземпляра объекта (или элементов массива). Он имеет следующий общий вид:
for each (переменнаяИлиЗначениеЭлемента in некийОбьект) { инструкции
}
Инструкции инструкции цикла f or-each-in выполняются один раз для каждой динамической переменной экземпляра или элемента массива в объекте некийОбьект. В процессе каждой итерации цикла значение переменной или элемент, над которым выполняется итерация (перечисление), присваивается переменной переменнаяИлиЗначениеЭлемента. Код внутри тела цикла имеет возможность применять это значение по своему усмотрению.
Например, рассмотрим описание объекта и связанный с ним цикл f or-each-in, продемонстрированный в следующем коде. Обратите внимание, что внутренний объект Object объявлен с использованием атрибута dynamic и, следовательно, поддерживает динамические переменные экземпляра.
var info:Object = new ObjectC ); info. city = «Toronto»; info. country = «Canada»;
for each (var detail:* in info) { trace(detail);
}
Приведенный цикл выполняется дважды, по одному разу для каждой из двух динамических переменных экземпляра объекта, на который ссылается переменная info. При выполнении цикла в первый раз переменной detail присваивается значение «Toronto» (то есть значение переменной city). При выполнении цикла во второй раз переменная detail содержит значение «Canada» (то есть значение переменной country). Таким образом, в результате выполнения цикла будет выведена следующая информация:
Toronto Canada
В большинстве случаев порядок, в котором циклы for-each-in и for-in перечисляют переменные объекта, не гарантирован. Однако существует два исключения: перечисление переменных экземпляров классов XML и XMLList происходит в возрастающем порядке, в соответствии с числовыми именами их переменных (то есть в соответствии с документированным порядком для объектов XML; дополнительную информацию можно найти в гл. 18). Для всех остальных типов объектов порядок перечисления, используемый циклами for-each-in и for-in, может отличаться в зависимости от версий языка ActionScript и клиентских сред выполнения Flash. Таким образом, не следует писать код, который зависит от порядка перечисления циклов for-each-in или for-in, кроме тех случаев, когда обрабатываются данные в формате XML.
Руководство по actionscript. часть 2, стр. 114
Следующий код демонстрирует цикл for-each-in, который используется для обращения к значениям элементов массива:
var games:Array = ["Project Gotham Racing", "Shadow of the Colossus", "Legend of Zelda"]:
for each (var game:* in games) { trace(game);
}
Приведенный цикл выполняется трижды, по одному разу для каждого из трех элементов массива games. При выполнении цикла в первый раз переменной game присваивается значение «Project Gotham Racing» (то есть значение первого элемента). При выполнении цикла во второй раз переменная game принимает значение «Shadow of the Colossus», а на третий раз — значение «Legend of Zelda». Таким образом, выводимая информация выглядит следующим образом:
Project Gotham Racing Shadow of the Colossus Legend of Zelda
Цикл for-each-in является напарником цикла for-in языка ActionScript. Тогда как цикл for-each-in перечисляет значения переменных, цикл for-in — имена переменных. Например, следующий цикл for-in перечисляет имена динамических переменных экземпляра объекта, на который ссылается переменная info:
for (var detailName:* in info) { trace(detailName);
}
// Вывод: // city // country
Обратите внимание, что предыдущий код выводит имена переменных city и country, а не их значения. Для обращения к значениям этих свойств мы могли бы использовать оператор [ ], который рассматривается далее, в разд. «Динамические обращения к переменным и методам». Это демонстрирует следующий код:
for (var detailName:* in info) { trace(i nfo[detai1 Name]);
}
// Вывод:
// Toronto // Canada
Чтобы исключить перечисление динамической переменной экземпляра в циклах for-in и for-each-in, используется метод setPropertylsEnumerable ( ) класса Ob j ect, показанный в следующем коде:
info. setPropertylsEnumerableC’city», false);
for (var detailName:* in info) { trace(info[detailName]);
}
// Выводит: Canada
// (переменная «city» не была обработана в цикле for-in)
Мы рассмотрим применение цикла for-each-in на практическом примере в разд. «Использование динамических переменных экземпляра для создания справочных таблиц».
Руководство по actionscript. часть 2, стр. 115
Динамическое добавление нового поведения в экземпляр
Когда вы познакомитесь с тем, как создавать динамические переменные экземпляра, у вас может возникнуть вопрос, поддерживает ли язык ActionScript и динамические методы экземпляра, то есть добавление нового метода экземпляра в один конкретный объект без добавления этого метода в любые другие экземпляры класса данного объекта. Фактически формальных способов для динамического добавления настоящего метода экземпляра в объект не существует. Тем не менее, присвоив замыкание функции динамической переменной экземпляра, мы можем имитировать эффект добавления нового метода в отдельный объект (чтобы освежить в памяти смысл фразы «замыкание функции», обратитесь к гл. 5). Следующий код демонстрирует общий подход:
некийОбъект. некаяДинамическаяПеременная = некаяФункция;
В приведенном коде некийОбъект — это экземпляр динамического класса, некаяДинамическаяПеременная — имя динамической переменной экземпляра (которая может являться либо новой, либо существующей переменной), а некаяФункция — ссылка на замыкание функции. Обычно функция некаяФункция задается с помощью литерала функции, как показано в следующем коде:
некийОбъект. некаяДинамическаяПеременная = function (параметр1, параметр2… параметрп) { II Тело функции
}
После того как функция некаяФункция будет присвоена динамической переменной экземпляра, она может быть вызвана через этот объект точно так же, как и обычный метод экземпляра, что показано в следующем коде:
некийОбъект. некаяДинамическаяПеременная(значение1, значение2. . . значениеп);
Чтобы продемонстрировать использование предыдущего общего синтаксиса, вернемся к примеру с переменной info из предыдущего раздела:
var info:Object = new Object( ); info. city = «Toronto»; info. country = «Canada»;
Предположим, мы хотим добавить в объект, на который ссылается переменная info, новое поведение — возможность форматировать и отображать собственное содержимое в виде строки. Мы создадим новую переменную экземпляра get Address, которой присвоим желаемую функцию форматирования, как показано в следующем коде:
info. getAddress = function ( ):String { return this. city + «, » + this. country;
}
Для вызова этой функции используется следующий код:
trace(info. getAddress( ));
Обратите внимание, что внутри тела функции, присвоенной переменной get Address, мы можем использовать ключевое слово this для обращения к переменным и методам объекта, через который вызывается эта функция. На самом деле в случае с замыканиями функций обратиться без использования ключевого слова this к переменным и методам объекта, через который вызывается эта функция, невозможно. Предположим, мы убрали ключевое слово this из описания функции getAddress ( ), как показано в следующем коде:
info. getAddress = function ( ):String { return city + «, » + country;
}
При поиске переменных city и country среда выполнения Flash не рассматривает автоматически переменные экземпляра объекта, на который ссылается переменная info. Следовательно, предыдущий код вызовет ошибку (если выше в цепочке видимости функции getAddress ( ) не существует других переменных с именами city и country).
Руководство по actionscript. часть 2, стр. 116
Если функция переменной экземпляра объекта присваивается внутри класса данного объекта, то она сможет обращаться к переменным и методам объекта, объявленным с использованием модификаторов управления доступом private, protected, internal и public. Если присваивание происходит внутри подкласса класса объекта, функция сможет обращаться только к тем переменным и методам объекта, которые объявлены с использованием модификаторов управления доступом protected, internal и public. Если присваивание происходит внутри пакета, в котором объявлен класс объекта, функция сможет обращаться только к переменным и методам объекта, объявленным с использованием модификаторов управления доступом internal и public. Если класс объекта объявлен в одном пакете, а присваивание происходит в другом пакете, функция сможет обращаться только к переменным и методам объекта, объявленным с использованием модификатора управления доступом public.
Например, рассмотрим следующий код, который создает динамический класс
Employee:
dynamic public class Employee { public var startDate:Date; private var age:int;
}
Следующий код присваивает функцию динамической переменной экземпляра doReport экземпляра класса Employee. Присваивание происходит за пределами класса Employee, но внутри класса, который объявлен в том же пакете, что и класс Employee. Следовательно, функция может обращаться только к тем переменным объекта Employee, которые объявлены с использованием модификаторов управления доступом internal й public. Переменные, объявленные с использованием модификаторов управления доступом protected и private, недоступны для этой функции.
public class Report { public function Report (employeeEmployee) { // Присваиваем функцию переменной doReport employee. doReport = function ( ):void { trace(this. startDate); // Обращение к public-переменной допустимо trace(this. age); // Обращение к private-переменной запрещено
Динамические обращения к переменным и методам
Поскольку имена динамических переменных экземпляра зачастую неизвестны вплоть до этапа выполнения программы, язык ActionScript предоставляет возможность указывать имя переменной с помощью обычного строкового выражения. Следующий код демонстрирует общий подход:
некийОбъект\_некоеВыражение]
В предыдущем коде некийОбъект — это ссылка на объект, к переменной которого происходит обращение, а некоеВыражение — любое выражение, возвращающее строку (которая обозначает имя переменной). Предыдущий код может применяться как для присваивания значения переменной, так и для получения значения.
Например, следующий код присваивает значение «Toronto» переменной, имя которой указывается с помощью выражения строкового литерала «city»:
var info:Object = new Object( ); info["city"] = «Toronto»;
Следующий код присваивает значение «Canada» переменной, имя которой указывается с помощью выражения строкового литерала «country»: info["country"] = «Canada»;
Следующий код получает значение переменной, имя которой указывается с помощью выражения-идентификатора detail:
var detail-.String = «city»; trace(info[detail]); // Выводит: Toronto
Когда среда выполнения Flash встречает код info [detail], она сначала определяет значение переменной detail (в нашем случае это «city»), после чего ищет переменную с именем «city» в объекте, на который ссылается переменная info.
Руководство по actionscript. часть 2, стр. 117
Синтаксические правила, применяемые к идентификаторам, не распространяются на переменные, которые создаются с использованием оператора [ ]. Например, следующий код создает динамическую переменную экземпляра, имя которой начинается с цифры:
var info:Object = new Object( ); info["411"] = «Information Line»;
Использование оператора «точка» (.) для создания той же переменной вызовет ошибку, поскольку такая запись нарушает синтаксические правила, применяемые к идентификаторам:
var info.-Object = new Object ( );
info.411 = «Information Line»; // ОШИБКА! Идентификаторы не должны
// начинаться с цифры
Стоит отметить, что описанная методика может использоваться для обращения не только к динамическим переменным экземпляра, но и к любым типам переменных и методов. Однако наиболее часто она применяется при работе именно с динамическими переменными экземпляра. Следующий раздел демонстрирует использование динамических обращений в практической ситуации: для создания справочной таблицы.
Использование динамических переменных экземпляра для создания справочных таблиц
Справочная таблица — это структура данных, которая связывает набор имен с соответствующим набором значений. Например, следующий псевдокод демонстрирует справочную таблицу, которая представляет меню (приводится по-русски):
закуска: маисовые чипсы
основное блюдо: лепешка с начинкой из бобов
десерт: пирожное
Чтобы представить данную справочную таблицу с помощью динамических переменных экземпляра, можно использовать следующий код:
var meal:0bject = new Object( ); meal. appetizer = «tortilla chips»; meal. maincourse = «bean burrito»; meal. dessert = «cake»;
Теперь рассмотрим более сложный сценарий. Представьте приложение для инвентаризации книг в книжном магазине, которое позволяет пользователю искать книги по номеру ISBN. Информация о каждой книге загружается с внешнего сервера. Чтобы минимизировать количество обращений к серверу, приложение загружает за раз информацию о 500 книгах. Для упрощения будем полагать, что информация о каждой книге представлена в виде отдельной строки, имеющей следующий формат:
«Price: $19.99. Title: Path of the Paddle»
Для хранения загруженной информации о книге в программе на языке ActionScript создадим экземпляр класса Ob j ect, который будет служить справочной таблицей для книг:
var bookList:Object = new 0bject( ):
Загруженную информацию о каждой книге мы присваиваем новой динамической переменной экземпляра созданного объекта bookList. Имя каждой переменной соответствует ISBN-номеру книги с предшествующей строкой «isbn». Например, переменная для книги с ISBN-номером 155209328Х будет называться isbnl5520 932 8X. Следующий код демонстрирует создание динамической переменной экземпляра для данной книги в том случае, если бы мы заранее знали ее ISBN-номер:
bookList. isbnl55209328X = «Price: $19.95. Title: Path of the Paddle»;
В реальном приложении тем не менее ISBN-номер книги станет известен только после того, как информация об этой книге будет загружена с сервера. Следовательно, имя динамической переменной экземпляра для каждой книги должно формироваться динамически, на основании загружаемых данных. Для демонстрационных целей создадим переменную bookDatа, значение которой будет представлять данные в том виде, в котором они были бы загружены с сервера. В этом упрощенном примере ISBN-номер и подробная информация о каждой книге разделяются одиночным символом «тильда» (-). При этом полная информация о каждой книге отделяется двойным символом «тильда»
var bookData:String = «155209328X~Price: $19.95. Title: Path of the Paddle» + «—»
+ «0072231726-Price: $24.95. Title: High Score!»;
Чтобы преобразовать загруженные данные о книгах из строки в массив книг для обработки, воспользуемся методом split ( ) класса String, как показано в следующем коде:
var bookDataArray. Array = bookData. split(«—»):
Для преобразования массива книг в справочную таблицу используем следующий код:
// Создаем переменную, которая будет хранить информацию о каждой книге // в процессе обработки var book:Array;
// Выполнить цикл один раз для каждого элемента в массиве книг for (var i:int = 0; i < bookdataarray.length; i++) {
// Преобразуем текущий элемент массива из строки в собственный массив. // Например, строка:
// «155209328X~Price: $19.95. Title: Path of the Paddle» // становится массивом:
// ["155209328X", "Price: $19.95. Title: Path of the Paddle"] book = bookDataArray[i].split(«~»);
// Создаем динамическую переменную экземпляра, имя которой соответствует
// ISBN-номеру текущего элемента в массиве книг, и присваиваем этой
// переменной описание текущего элемента в массиве. Обратите внимание, что
// ISBN-номер представлен выражением book[0], а описание -
// выражением Ьоок[1].
Руководство по actionscript. часть 2, стр. 118
Bookl_ist["isbn" + book[0]] = book[l];
}
Когда все 500 книг будут добавлены в объект bookList и каждая из них будет храниться в своей собственной динамической переменной экземпляра, пользователь сможет выбирать книгу для просмотра, вводя ее ISBN-номер в текстовое поле isbnlnput. Вот как бы мы отображали информацию о выбранной пользователем книге в процессе отладки:
trace(bookList["isbn" + isbnlnput. text]);
Следующим же образом мы бы выводили информацию о выбранной пользователем книге на экране в текстовом поле, на которое ссылается переменная
bookDescription:
bookDescription. text = bookl_ist["isbn" + isbnlnput. text];
Для отображения списка всех книг, хранящихся в объекте bookList, можно использовать цикл for-each-in, как показано в следующем коде:
for each (var booklnfo:* in bookList) { // Выводим значение динамической переменной экземпляра, // обрабатываемой в текущий момент trace(booklnfo);
}
В результате выполнения цикла будет выведена следующая отладочная информация:
Price: $19.95. Title: Path of the Paddle Price: $24.95. Title: High Score!
Создание справочных таблиц с помощью литералов объекта. Для удобства справочные таблицы, содержимое которых имеет фиксированный размер и известно заранее, можно создавать с помощью литерала объекта. Литерал объекта создает новый экземпляр класса Ob j ect из набора пар «имя/значение», которые представляют динамические переменные экземпляра, разделены запятыми и заключены в фигурные скобки. Вот общий синтаксис:
{имяПеременной1:значениеПеременной!, имяПеременной2:значениеПеременной2,
имяПеременнойМ:значениеПеременнойИ}
Например, следующий код создает экземпляр класса Ob j ect с динамической переменной экземпляра city (значением которой является «Toronto») и динамической переменной экземпляра country (значением является «Canada»):
var info:Object = {city:»Toronto», country:»Canada»};
Данный код идентичен следующему:
var info:Object = new Object( ); info. city = «Toronto»; info. country = «Canada»;
Если бы в приложении для инвентаризации из предыдущего раздела было всего две книги, мы могли бы использовать следующий литерал объекта для создания справочной таблицы bookList:
var bookList:Object = {
isbnl55209328X:»Price: $19.95. Title: Path of the Paddle».
Руководство по actionscript. часть 2, стр. 119
Isbn0072231726:»Price: $24.95. Title: High Score!»
}:
Использование функций для создания объектов
В процессе чтения этой книги вы видели, что большинство объектов в языке ActionScript создается с помощью классов. Тем не менее существует возможность создавать объекты с использованием независимых замыканий функций. Следующий код демонстрирует общий подход. В нем для примера объявлена функция Employee ( ), которая применяется для создания объекта:
// Создаем функцию function Employee ( ) { }
// Используем функцию для создания объекта и присваиваем этот объект
// переменной worker
var worker = new Employee( );
Обратите внимание, что переменная worker является нетипизированной. С точки зрения типов данных объект, на который ссылается переменная worker, представляет собой экземпляр класса Object. Класс Employee не существует, поэтому не существует и тип данных Employee. Таким образом, следующий код вызовет ошибку (поскольку тип данных Employee не существует):
// ОШИБКА!
var worker-.Employee = new Employee( );
Замыкание функции, используемое для создания объекта, называется функцией-конструктором (не путайте с л*еяюдо. м-конструктором, который является частью определения класса). В языке ActionScript 3.0 независимые функции, объявленные на уровне пакета, не могут применяться в качестве функций-конструкторов, ио: этому предыдущий код должен быть размещен внутри метода, в коде за пределами описания пакета или в сценарии кадра на временной шкале в среде разработки Flash. Тем не менее ради краткости в этом разделе все функции-конструкторы
показаны без необходимых методов или сценариев кадров, которые должны содержать эти функции.
Руководство по actionscript. часть 2, стр. 120
Все объекты, создаваемые из функций-конструкторов, неявно являются динамическими. Таким образом, в процессе создания объекта функция-конструктор может использовать ключевое слово this для добавления в этот объект новых динамических переменных экземпляра. Динамическим переменным экземпляра, создаваемым в функции-конструкторе, обычно присваиваются значения, которые передаются в функцию в качестве аргументов. Это демонстрирует следующий код:
function Employee (age, salary) { // Описываем динамические переменные экземпляра this. age = age: this. salary = salary;
}
// Передаем аргументы, которые будут использованы в качестве значений // динамических переменных экземпляра данного объекта var worker = new Employee(25, 27000); trace(worker. age); // Выводит: 25
Чтобы объекты, создаваемые с помощью определенной функции-конструктора, могли разделять между собой информацию и поведение, язык ActionScript для каждой функции вводит специальную статическую переменную prototype. Переменная функции prototype ссылается на объект (называемый объектом-прототипом функции), к динамическим переменным экземпляра которого можно обращаться через любой объект, созданный с помощью данной функции. Изначально язык ActionScript присваивает переменной prototype каждой функции экземпляр базового класса Object. Добавляя в этот объект динамические переменные экземпляра, мы можем создать информацию и поведение, разделяемые между всеми объектами, созданными из конкретной функции.
Например, следующий код добавляет динамическую переменную экземпляра company в объект prototype функции Employee ( ):
Employee. prototype. company = «AnyCorp»;
В результате любой объект, созданный из функции Employee ( ), может обращаться к переменной company так, будто это его собственная динамическая переменная экземпляра:
var worker = new Employee(25, 27000); trace(worker. company); // Выводит: AnyCorp
В предыдущем коде, когда среда выполнения Flash поймет, что объект, созданный из функции Employee ( ) (worker), не имеет переменной экземпляра или метода экземпляра с именем «company», она проверит, определена ли динамическая переменная экземпляра с таким именем в объекте Employee. prototype. В этом объекте такая переменная определена’ поэтому среда Flash использует ее так, будто это собственная переменная объекта worker.
Если, с другой стороны, в объекте worker определена собственная переменная company, она будет использована вместо переменной объекта Employee. prototype. Это демонстрирует следующий код:
var worker = new Employee(25, 27000); worker. company = «CarCompany»;
trace(worker. company); // Выводит: CarCompany (не AnyCorp)
Используя методику, рассмотренную в разд. «Динамическое добавление нового поведения в экземпляр», мы можем присвоить функцию динамической переменной экземпляра объекта prototype любой функции-конструктора. Эта функция в дальнейшем может быть использована любым объектом, созданным из данной функции-конструктора.
Руководство по actionscript. часть 2, стр. 121
Например, следующий код определяет динамическую переменную экземпляра getBonus в объекте prototype функции Employee ( ) и присваивает этой переменной функцию, которая подсчитывает и возвращает премию по итогам года:
Employee. prototype. getBonus = function (percentage:int).-Number { // Возвращает размер премии в зависимости от зарплаты сотрудника // и указанного процента return this. salary * (percentage/100);
}
В результате все объекты, созданные из функции Employee ( ), могут использовать функцию getBonus ( ) так, будто она была присвоена их собственным динамическим переменным экземпляра:
var worker = new Employee(25, 27000); trace(worker. getBonusdO)); // Выводит: 2700
Использование объектов-прототипов для дополнения классов
Как уже было сказано, язык ActionScript определяет для каждой функции специальную статическую переменную prototype. Используя переменную prototype функции, мы можем разделять информацию и поведение между всеми объектами, созданными из этой функции.
Подобно тому, как язык ActionScript определяет переменную prototype для каждой функции, он также определяет статическую переменную prototype для каждого класса. Используя статическую переменную prototype, мы можем добавлять разделяемую между всеми экземплярами данного класса информацию и поведение на этапе выполнения программы.
Например, следующий код определяет новую динамическую переменную экземпляра isEmpty в объекте prototype внутреннего класса String и присваивает ей функцию. Эта функция возвращает значение true, когда строка не содержит символов; в противном случае функция возвращает значение false:
String. prototype. isEmpty = function ( ) { return (this == «») ? true : false;
}:
Для вызова функции isEmpty ( ) над объектом String мы используем следующий код:
var si:Stri ng = «Hello World»; var s2:String = «»;
trace(sl. isEmpty( )); // Выводит: false trace(s2.isEmpty( )); // Выводит: true
Тем не менее в предыдущем примере кода — и вообще во всей этой методике — существует проблема: динамическая переменная экземпляра добавляется только на этапе выполнения программы; следовательно, компилятор не имеет ни малейшего представления о том, что эта переменная существует, и сгенерирует ошибку, если компиляция программы будет выполняться в строгом режиме. Например, при компиляции программы в строгом режиме код из предыдущего примера вызовет следующую ошибку:
Call to a possibly undefined method isEmpty through a reference with static type String.