Беларусь, рогачёв |
Функции
Объект 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.