Московский физико-технический институт
Опубликован: 23.12.2005 | Доступ: свободный | Студентов: 2765 / 183 | Оценка: 4.61 / 4.44 | Длительность: 27:18:00
ISBN: 978-5-9556-0051-2
Лекция 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.