Функции как объекты

Функции — это чрезвычайно специфичные структуры ActionScript. В самом начале главы мы определили их как подпрограммы. Однако это лишь одна (пусть и самая важная) из ипостасей функций. В зависимости от типа решаемой задачи пол словом «функция» могут подразумеваться три (!) различные структуры данных:

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

• Функция как объект активации. Объект активации, как вы уже знаете, создается при вызове функции. Его роль сводится к хранению локальных переменных, параметров и некоторых служебных свойств. Говоря «массив аргументов функции», подфункцией мы понимаем объект активации.

• Функция как объект. Это проявление функции нам пока малознакомо. Изучению его особенностей будет посвящен данный раздел.

Каким это ни странным может показаться на первый взгляд, но в ActionScript Функции являются объектами. Иными словами, им можно ставить в соответствие свойства и методы, можно управлять характером присущего им наследования, можно присваивать их переменным или же просто удалять их.


Пример:

// Функция, выводящая имена всех своих доступных для перечисления свойств
// со значениями
var func:Function=function():Void {
// Просматриваем свойства вызванной функции
for (var i in arguments.callee) {
// Выводим имя свойства и его значение
trace (i + "=" +arguments .callee [i]);
func.a-1; // Определяем пользовательские свойства
func.b=2;
func(); // Выводит: b=2 a=l

Класс Function, к которому относятся функции, динамический. Поэтому в процессе проигрыванияфильма, функции можно присвоить новое свойство или метод. Важно понять, что функция как объект и функция как подпрограмма связаны очень и очень слабо. Объект функции — это просто удобный инструмент, обеспечивающий дополнительную стройность языка. Функции как подпрограммы могут быть реализованы и без привязывания к ним объекта, но тогда многие концепции ActionScript станут невозможными.

То, что функции соответствует объект, совсем не означает, что данный объект хранит прописанный в ее определении код. Это заблуждение весьма характерно для новичков и связано с тем, что в сценарии определение функции представляется единой структурой, На самом же деле объект функции существует практически независимо от ее кода.


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

function func():Void {} ;
trace(func); // Выводит: [type Function]

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

Почему функцию можно присвоить переменной? Потому что при этом присваивается ссылка на объект функции, а не на сегмент кода. Вызов же функции через переменную означает активацию нужного внутреннего метода ее объекта.

Доказательством того, что объект функции и ее исполнимый код не имеют тесной связи, является то, что сам объект (а следовательно, его свойства и методы) не «виден» из кода:

function func():Void {
trace(prop);
}
func.prop = "Привет";
func(); // Выводит: undefined

Объект функции хранит несколько специфичных внутренних свойств.


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

Объем памяти, занимаемый объектом функции, совершенно не зависит от длины кода в блоке ее определения. Это означает, что экономить оперативную память, сокращая число объектов функции, — это не самый эффективный ход. Кстати, из того, что объект функции и ее код существуют достаточно независимо друг от друга, вытекает еще одно важное следствие. Удаление объекта функции не будет сопровождаться удалением соответствующего кода. Он будет находиться в памяти рее время проигрывания фильма. Представление функции в качестве объекта чрезвычайно важно в ActionScript. Так на нем базируются основные концепции объектно-ориентированного программирования, без него были бы невозможны многие полезные инструменты языка. Однако нужно четко понимать разницу между функцией как объектом и как подпрограммой.

Класс Function
Функции являются объектами. Но объектами крайне специфичными. Поэтому достаточно логичнопредполагать, что в ActionScript они должны относиться к особому классу. И такой класс действительно существует.

Стандарт ЕСМА-262 предусматривает наличие класса Function, к которому принадлежат все
функции. Используя конструктор этого класса, можно создавать новые функции — но иначе, чем с применением ключевого слова function. Список параметров и выражения тела функции не набираются непосредственно, а в строковом представлении передаются в качестве аргументов конструктору Function():

var func=new Function("a", "с", "return a+b+c");
func(1,1,1);
// В JavaScript возвращает 3, в ActionScript - undefined

Такой способ задания функций имеет огромные преимущества перед традиционным ввиду предельной обобщенности. Так, текст функции может поступить во время работы сценария из текстового поля или внешнего файла, что делает код чрезвычайно пластичным. Например, можно написать программу, считающую интеграл от произвольной функции. При этом задача пользователя сведется только к набору вида функции и заданию пределов в специальных полях. К сожалению, ActionScript не поддерживает конструктор Function(). И причина та же, что и лежащая в основе ограниченности функции eval(): отсутствие возможности динамической компиляции кода. Требования к размеру Flash-плейера слишком жесткие, чтобы разработчики могли позволить роскошь включения в него транслятора языка.

Таким образом, ActionScript поддерживает не все прописанные ЕСМА-262 возможности класса Function. Да и классом его можно назвать лишь с очень большой натяжкой, так как в нем не имеется главного атрибута — конструктора. Однако, так как функции наследуют методы его прототипа, Function не является простым встроенным объектом. Удобства ради мы будем все же считать его классом.

function func():Void{};
trace(func instanceof Function);
// Выводит: true (func принадлежит к классу Function)

Сохранив метод или свойство в прототипе объекта Function, вы сделаете его доступным для всех функций. Однако вряд ли можно придумать востребованную практикой задачу, в которой такой ход понадобился бы. Гораздо больший интерес представляют встроенные свойства и методы класса Function.

Свойство prototype
ActionScript является объектно-ориентированным языком. Однако в нем нет классов в том понимании, в каком они реализованы в языках Java, C++ или Smalltalk. Объекты создаются конструкторами исходя из введенного в их блоке кода. Конструкторы — это обычные функции, используемые с предложением new. Например, глобальная функция String(), заданная с new, создает объекты класса String. Создать подобным образом можно и пользовательский объект:

// Конструктор Ball создает мячи нужного цвета и размера
function Ball(color:String,size:Number):Void {
// Присваиваем свойствам объекта нужные значения
this.color=color;
this.size=size;
}
var bigRedBall=new Ball("red",1000); // Создаем большой красный мяч
trace(bigRedBall.color); // Выводит: red (мяч действительно красный)
trace(typeof bigRedBall) // Выводит: object (мяч является объектом)

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

Прототип — это свойство объекта функции, которое автоматически создается для того, чтобы она могла использоваться в качестве конструктора класса. Свойство prototype хранит объект, который может быть носителем присущих классу свойств и методов, Любой экземпляр имеет особое свойство __proto__, указывающее на прототип его конструктора. Если интерпретатор не обнаруживает вызываемое свойство в самом объекте, он следует по хранящейся в нем ссылке и обследует прототип класса. В свою очередь объект прототипа может указывать на прототип класса, который является надклассом данного. Если интерпретатор не найдет необходимого элемента в первом прототипе, он будет просматривать второй — и так до тех пор, пока не будет пройдена вся цепочка прототипов, обеспечивающая наследование для данного объекта.

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

В качестве примера использования свойства prototype конструктора встроенного класса Action-Script приведем реализацию математического метода, переводящего градусы в радианы (чтобы использовать этот метод, придется преодолеть сложность, связанную с тем, что класс Number не является динамическим. Чтобы это сделать, для доступа к методу нужно использовать не оператор точки, а оператор «[]»).

// Сохраняем функцию как метод, наследуемый числовыми объектами
Number.prototype.deg2rad = function():Number {
return this/180*Math.PI;
};
var deg:Number=360;
trace(deg["deg2rad"]()); // Выводит: 6.28318530717959

Если вы не все поняли изданного предельно краткого описания принципов наследования в Action-Script, не огорчайтесь. В свое время мы посвятим вопросу объектно-ориентированного программирования целую главу. А пока можете считать прототип просто местом, в котором хранятся методы и свойства классов.

Методы вызова функции
К классу Function относятся предназначенные для осуществления вызова функции методы: apply() и call(). Вы можете спросить, зачем использовать такие громоздкие и сложные конструкции, как методы, если вызвать функцию можно, применив оператор «()». Дело в том, что возможности методов apply() и call() и оператора вызова функции далеко не идентичны. Использование методов позволяет производить активацию функции, базируясь на куда менее явных данных, чем того требует оператор «()» (например, можно активизировать непосредственно литерал функции или передать ей произвольное число параметров). А это важно, если исполняющийся код должен быть чувствителен к контексту.
Метод apply() имеет следующий общий синтаксис:

functionObjееt.apply(thisObject, [par]),

где functionObject — функция, которая должна быть вызвана (может быть как пользовательской, так и встроенной); thisobject — объект, на который будет указывать ссылка this в теле функции. Определение этого параметра важно, если вызываемая функция является методом. Если активизируется обычная функция, то thisobject необходимо поставить в соответствие null; par — массив, содержащий передаваемые функции параметры.В чем заключаются преимущества метода apply() по сравнению с оператором «()»? Во-первых, используя его, вы можете передавать вызываемой функции различное число параметров, ничего не изменяя в коде. Во-вторых, apply() позволяет применять метод к объекту, даже если он его непосредственно не вызывает:

// Метод, изменяющий размер клипа
MovieClip.prototype.sizer = function(proc:Number):Void {
this._xscale=proc;
this._Yscale=proc;
};
ball.sizer(200); // Стандартный способ вызова метода
_root.sizer.apply(ball, [200]); // Метод вызывает _root, а применяется
// он к ball

Метод call() практически полностью идентичен apply() за одним малым исключением: передаваемые параметры не нужно заносить в массив, а достаточно просто перечислить их через запятую.

Общий синтаксис:

FunctionObject.call(thisObject, par1, par2, par3...)

Метод call() рекомендуется использовать в тех случаях, когда функция не требует задания аргументов.

}
push 'a'
getVariable
return
@5 end

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела Action Script на эту тему:
Особенности цепочки областей видимости функций
Предложение return

Вернуться в раздел: Action Script / 4. Функции