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

Функции

Объект arguments

Программисты, пишущие на С (а уж тем более - на ассемблере) хорошо знакомы с так называемым стеком вызовов. Под этот стек отводится специальная область памяти, в которой указываются адреса вызывающих и вызываемых функций, а также передаваемые в них аргументы. Если есть желание работать на низком уровне, то покопавшись в стеке, можно выяснить, кто вызвал ту функцию, внутри которой мы в данный момент находимся, какие аргументы ей передал, у этой вызвавшей функции тоже определить аргументы и вызывающую функцию и т.д. Это позволяет делать разные трюки, самым распространенным из которых является функция с переменным числом аргументов. Оказывается, Флэш-программисты в этом отношении ничуть не обделены, и у них тоже есть возможность получить всю подобную информацию. И для этого существует специальный объект по имени arguments , который определен внутри каждой функции.

Сущность объекта arguments

Объект arguments - это массив, содержащий в себе все аргументы функции (не формальные параметры, а именно те, что пользователь передал в нее в данном вызове). Как любой массив, arguments имеет поле length, в котором записана его длина. Кроме того, arguments имеет еще два поля: caller и callee, через которые можно получить доступ к объектам вызывающей и вызываемой функций. Подробнее об этом мы расскажем чуть ниже. (Этот рассказ будет тем более полезен, что не вся информация в справочной документации Флэш МХ соответствует действительности.) А теперь попробуем воспользоваться полученными сведениями об объекте arguments для того, чтобы сделать функцию с произвольным числом аргументов.

Функции с произвольным числом аргументов

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

_global.joinBy = function(glue){
   var tmpString = ((arguments[1] != undefined) ? arguments[1] :
"");
   for (var i = 2; i<arguments.length; i++){
      tmpString += glue + arguments[i];
   }
   return tmpString;
}
trace("result = " + joinBy("___"));
trace("result = " + joinBy("___", "r"));
trace("result = " + joinBy("___", "E", "R", "T", "G"));

Выводит этот код следующие бессмертные строки:

result =
result = r
result = E___R___T___G

Впрочем, то же самое можно сделать и чуть покороче (хотя, возможно, и менее наглядно):

_global.joinBy1 = function(glue){
   if (arguments.length == 2) return arguments[1];
   var tmpString = arguments.join(glue);
   return tmpString.substr(tmpString.indexOf(glue) +
glue.length);
}
trace("result = " + joinBy1("___"));
trace("result = " + joinBy1("___", "r"));
trace("result = " + joinBy1("___", "E", "R", "T", "G"));

Результат получается тот же самый.

Обратите внимание, что arguments - это самый настоящий массив, у которого, в частности, работает метод join. Хотя по этому поводу и могут возникнуть небольшие сомнения, когда Флэш МХ автоматически подсказывает нам поля и методы, которые можно выбрать у объекта arguments - в этом списке только length, caller и callee. Но сомнения напрасны, все методы, относящиеся к массивам, прекрасно работают и с массивом arguments . Его можно даже отсортировать, хотя не так уж просто представить себе задачу, в которой это было бы необходимо.

caller и callee

Как мы уже писали, поля caller и callee позволяют получить доступ к объектам вызывающей и вызываемой функций. В справочной документации по Флэш МХ написано, правда, по-другому. Там сказано, что, в то время как поле callee действительно дает доступ к объекту вызываемой функции (то есть именно той, внутри которой мы и получаем доступ к объекту arguments ), поле caller дает доступ к объекту arguments вызывающей функции. Если бы все было именно так, это было бы хорошо. Мы имели бы возможность получить доступ к полному списку аргументов вызывающей функции, сам объект вызывающей функции надо было бы получать через arguments.caller.callee, зато можно было бы п олностью раскрутить стек вызовов ( arguments.caller.caller.caller и т.д.). К сожалению, дела обстоят вовсе не так радужно, как утверждает документация. И arguments.caller указывает именно на объект вызывающей функции, добраться же до объекта arguments вызывающей функции мы не сможем никак (если только нам его не передадут специально в качестве аргумента вызываемой функции ). Таким образом, область применения поля caller не очень широка. В следующих лекциях мы приведем пример использовании caller в одной из специфических форм наследования. А пока что - вот пример использования caller для отладочных целей. Если мы заранее позаботимся о снабжении каждой функции полем name, то отладочная функция сможет выводить эту информацию в консоль:

_global.traceDebugInfo = function(){
   trace("---------------");
   trace("function: " + arguments.caller.name);
   trace("a = " + a);
   trace("---------------");
}
_global.doSomething = function(){
   a++;
   traceDebugInfo();
}
_global.doSomething.name = "doSomething";
_global.doAnything = function(){
   a+=4;
   traceDebugInfo();
}
_global.doAnything.name = "doAnything";
a = 3;
traceDebugInfo();
doAnything();
doSomething();
doAnything();

Приведенный здесь код выводит

---------------
function:
a = 3
---------------
---------------
function: doAnything
a = 7
---------------
---------------
function: doSomething
a = 8
---------------
---------------
function: doAnything
a = 12
---------------

Обратите внимание, что мы можем вызвать нашу функцию traceDebugInfo() в любом месте - не только в специально приспособленной функции. (В нашей программе она была вызвана "снаружи" всех функций в самом начале). В этом случае мы просто не получаем информации о вызывающей функции, но все прочие операции по выводу отладочных сообщений нормально работают. Так что отладочные функции подобного типа - это неплохая идея, даже если вы не всегда снабжаете прочие ваши функции отладочными полями вроде name.

алексеи федорович
алексеи федорович
Беларусь, рогачёв
Тамара Ионова
Тамара Ионова
Россия, Нижний Новгород, НГПУ, 2009