Беларусь, рогачёв |
Функции
Функция как объект
Вы наверняка помните, что среди небольшого количества типов данных, поддерживаемых во Флэш МХ, имеется тип Function . А это значит, что мы можем создавать объекты этого типа и переменные, ссылающиеся на эти объекты. Наконец, каждый тип данных во Флэш МХ предназначен для того, чтобы с ним особым образом работали некоторые операторы - для функционального типа это круглые скобки, оператор вызова функции. Сейчас мы упорядочим наши знания о способах определения функций, используя этот взгляд на вещи.
Разные ссылки на одну функцию
Итак, в некоторой переменной может лежать ссылка на объект-функцию и круглые скобки можно использовать для вызова этой функции.
Подтвердить такую точку зрения может использование нескольких ссылок на одну функцию. Вот как это делается:
var a = function(){ trace("function a called"); } var b = a; a(); b();
(Переменные, в которых хранятся ссылки на функции, мы сделали локальными на всякий случай: когда есть вероятность, что какой-то кусок кода будет потом помещен в функцию, будет нехорошо, если эта функция станет плодить вокруг себя массу переменных, а то и портить уже существующие. Так что хорошим стилем будет писать var всегда, когда это не противоречит вашим намерениям.)
Этот код, как и следовало ожидать, выводит:
function a called function a called
Здесь мы определяли функцию "на лету". Сейчас мы можем догадаться, что такой способ определения всего лишь приводит к генерации временной ссылки на функцию, которую мы затем записываем в именованную переменную (а можем, например, передать в другую функцию в качестве аргумента). Это наводит на мысль, что и на функции, определенные заранее, тоже можно делать альтернативные ссылки.
var d = c; d(); function c(){ trace("function c called"); }
Этот код, хотя и не является интуитивно понятным, все же выводит именно
function с called
Мы еще раз убеждаемся в том, что функции, определенные заранее, действительно определяются перед выполнением всего остального кода.
Передача функций в качестве аргумента
Про эту красивую возможность мы не раз уже говорили. Пора, наконец-то, привести простой и наглядный пример.
var d = ["a", "b", "c", "d"]; iterate(d, doubleString); trace(d.join(", ")); iterate(d, capitalize); trace(d.join(", ")); function iterate(arr, func){ for (var i=0; i<arr.length; i++) arr[i] = func(arr[i]); } function doubleString(str){ return str+str; } function capitalize(str){ return str.toUpperCase(); }
Этот код выводит
aa, bb, cc, dd AA, BB, CC, DD
В этом примере, кроме передачи функции как аргумента, мы также в очередной раз продемонстрировали особенности определения функций заранее. Этот прием имеет ряд преимуществ: например, если сущность функций очевидна, то код, делающий основную работу можно поместить в начале экрана. Однако определение "на лету" имеет более важные преимущества при использовании во включаемых файлах и при помещении функций в объект _global (откуда они, напомним, доступны для любого _level, то есть всем загруженным *.swf-роликам. Причем, если нет конфликта имен, доступны без явного указания _global ). Поэтому мы, как правило, будем использовать именно определение "на лету".
Возвращаемое значение типа Function. Куда указывает this
Мы уже видели, что любая ссылка может указывать на функцию. В частности - ссылка, возвращаемая другой функцией в операторе return. Получив эту ссылку, мы можем немедленно вызвать ту функцию, которую нам вернули (получается забавный синтаксис со стоящими подряд парами открывающих и закрывающих фигурных скобок). А можем присвоить это значение какой-нибудь переменной. Интересно также рассмотреть разницу между тем, куда указывает в функциях, сгенерированных внутри других функций, ссылка this . Вот пример кода, который позволит ответить на наши вопросы.
t1 = "Снаружи функции"; function test(arg){ var t1 = "Внутри функции"; var func = function(){ trace("this.arg = " + this.arg); trace("this.t1 = " + this.t1); trace("t1 = " + t1); } trace("---- Вызываем func() ------"); func(); trace("------------------"); return func; } trace("======= Вызываем test()() =============="); test()(); trace(""); trace("======= Вызываем а = test(); a() ======="); a = test("argString"); a();
На выходе получаем:
======= Вызываем test()() ============== ---- Вызываем func() ------ this.arg = this.t1 = Внутри функции t1 = Внутри функции ------------------ this.arg = this.t1 = t1 = Внутри функции ======= Вызываем а = test(); a() ======= ---- Вызываем func() ------ this.arg = argString this.t1 = Внутри функции t1 = Внутри функции ------------------ this.arg = this.t1 = Снаружи функции t1 = Внутри функции
Выводы отсюда можно сделать следующие.
- Возвращать из функции другую функцию можно.
- this внутри функции действительно всегда указывает на тот объект, к которому принадлежит ссылка, через которую вызвана функция.
- Если ссылка является локальной переменной другой (генерирующей нашу) функции - она принадлежит объекту, описывающему контекст вызова функции. В этом объекте лежат все локальные переменные и аргументы. (Но не путайте это с объектом arguments , о котором речь пойдет дальше: arguments, - это массив и аргументы лежат в нем по номерам). На этот-то объект, описывающий контекст вызова, и указывает this в сгенерированной функции (если ссылка на эту функцию сохранена в локальной переменной ).
- Наконец, если функция вызывается немедленно по получении ссылки на нее (в нашем примере это делает оператор test()(); ), то ссылка эта, видимо, хранится в каком-то временном объекте. Во всяком случае, это и не внешний объект и не контекст вызова test, поскольку и в том и в другом мы заводили переменную t1 ; а результат вызова test()(); говорит об отсутствии этой переменной в обсуждаемом объекте.