Опубликован: 23.12.2005 | Уровень: специалист | Доступ: платный | ВУЗ: Московский физико-технический институт
Лекция 5:

Функции

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

Вы наверняка помните, что среди небольшого количества типов данных, поддерживаемых во Флэш МХ, имеется тип 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 = Внутри функции

Выводы отсюда можно сделать следующие.

  1. Возвращать из функции другую функцию можно.
  2. this внутри функции действительно всегда указывает на тот объект, к которому принадлежит ссылка, через которую вызвана функция.
  3. Если ссылка является локальной переменной другой (генерирующей нашу) функции - она принадлежит объекту, описывающему контекст вызова функции. В этом объекте лежат все локальные переменные и аргументы. (Но не путайте это с объектом arguments , о котором речь пойдет дальше: arguments, - это массив и аргументы лежат в нем по номерам). На этот-то объект, описывающий контекст вызова, и указывает this в сгенерированной функции (если ссылка на эту функцию сохранена в локальной переменной ).
  4. Наконец, если функция вызывается немедленно по получении ссылки на нее (в нашем примере это делает оператор test()(); ), то ссылка эта, видимо, хранится в каком-то временном объекте. Во всяком случае, это и не внешний объект и не контекст вызова test, поскольку и в том и в другом мы заводили переменную t1 ; а результат вызова test()(); говорит об отсутствии этой переменной в обсуждаемом объекте.
алексеи федорович
алексеи федорович
Беларусь, рогачёв
Тамара Ионова
Тамара Ионова
Россия, Нижний Новгород, НГПУ, 2009