В JavaScript любая функция может быть вызвана с произвольным количеством аргументов.
Например:
В некоторых языках программист может создать две функции с одинаковым именем, но разным набором аргументов, а при вызове интерпретатор сам выберет нужную:
Это называется «полиморфизмом функций» или «перегрузкой функций». В JavaScript ничего подобного нет.
Может быть только одна функция с именем log
, которая вызывается с любыми аргументами. А уже внутри она может посмотреть, с чем вызвана и по-разному отработать.
В примере выше второе объявление log
просто переопределит первое.
Доступ к «лишним» аргументам
Как получить значения аргументов, которых нет в списке параметров?
Доступ к ним осуществляется через «псевдо-массив» arguments.
Он содержит список аргументов по номерам: arguments[0]
, arguments[1]
…, а также свойство length
.
Например, выведем список всех аргументов:
Все параметры находятся в arguments
, даже если они есть в списке. Код выше сработал бы также, будь функция объявлена sayHi(a,b,c)
.
В старом стандарте JavaScript псевдо-массив arguments
и переменные-параметры ссылаются на одни и те же значения.
В результате изменения arguments
влияют на параметры и наоборот.
Например:
Наоборот:
В современной редакции стандарта это поведение изменено. Аргументы отделены от локальных переменных:
Если вы не используете строгий режим, то чтобы переменные не менялись «неожиданно», рекомендуется никогда не изменять arguments
.
Частая ошибка новичков — попытка применить методы Array
к arguments
. Об этом говорит сайт https://intellect.icu . Это невозможно:
Дело в том, что arguments
— это не массив Array
.
В действительности, это обычный объект, просто ключи числовые и есть length
. На этом сходство заканчивается. Никаких особых методов у него нет, и методы массивов он тоже не поддерживает.
Впрочем, никто не мешает сделать обычный массив из arguments
:
Пример использования: copy(dst, src1,...)
Иногда встает задача — скопировать в существующий объект свойства из одного или нескольких других.
Напишем для этого функцию copy
. Она будет работать с любым числом аргументов, благодаря использованию arguments
.
Синтаксис:
- copy(dst, src1, src2…)
- Копирует свойства из объектов
src1, src2,...
в объект dst
. Возвращает получившийся объект.
Использование:
- Для добавления свойств в объект
user
:
Использование copy
позволяет сократить код, который потребовался бы для ручного копирования свойств:
Объект user
пишется только один раз, и общий смысл кода более ясен.
- Для создания копии объекта
user
:
var userClone = copy({}, user); |
Такой «клон» объекта может пригодиться там, где мы хотим изменять его свойства, при этом не трогая исходный объект user
. В нашей реализации мы будем копировать только свойства первого уровня, то есть вложенные объекты не обрабатываются. Впрочем, ее можно расширить.
Теперь перейдем к реализации.
Первый аргумент у copy
всегда есть, поэтому укажем его в определении. А остальные будем получать из arguments
, вот так:
2 |
for ( var i=1; i<arguments.length; i++) { |
3 |
var obj = arguments[i]; |
При желании, такое копирование можно реализовать рекурсивно.
arguments.callee
и arguments.callee.caller
Объект arguments
не только хранит список аргументов, но и обеспечивает доступ к ряду интересных свойств. В современном стандарте JavaScript они отсутствуют, но часто встречаются в старом коде.
arguments.callee
Свойство arguments.callee
содержит ссылку на функцию, которая выполняется в данный момент.
Это свойство устарело. Современная спецификация рекомендует использоватьименованные функциональные выражения (NFE).
Браузеры могут более эффективно оптимизировать код, если arguments.callee
не используется.
Тем не менее, свойство arguments.callee
зачастую удобнее, так как функцию с ним можно переименовывать как угодно и не надо менять ничего внутри. Кроме того, NFE некорректно работают в IE<9.
Например:
Зачем нужно делать какое-то свойство callee
, если можно использовать просто f
? Чтобы это понять, рассмотрим несколько другой пример.
В JavaScript есть встроенная функция setTimeout(func, ms)
, которая вызывает func
через ms
миллисекунд, например:
Функция, которую вызывает setTimeout
, объявлена как Function Expression
, без имени.
А что если хочется из самой функции вызвать себя еще раз? По имени-то обратиться нельзя. Как раз для таких случаев и придуман arguments.callee
, который гарантирует обращение изнутри функции к самой себе.
То есть, рекурсивный вызов будет выглядеть так:
Аргументы можно передавать в arguments.callee()
так же, как в обычную функцию.
Пример с факториалом:
2 |
var factorial = function (n) { |
3 |
return n==1 ? 1 : n*arguments.callee(n-1); |
Функция
factorial
не использует свое имя внутри , поэтому рекурсивные вызовы будут идти правильно, даже если функция «уехала» в другую переменную.
Рекомендованной альтернативой arguments.callee
являются именованные функциональные выражения.
arguments.callee.caller
Свойство arguments.callee.caller
хранит ссылку на функцию, которая вызвала данную.
Это свойство устарело, аналогично arguments.callee
.
Также существует похожее свойство arguments.caller
(без callee
). Оно не кросс-браузерное, не используйте его. Свойство arguments.callee.caller
поддерживается везде.
Например:
На практике это свойство используется очень редко, например для получения информации о стеке (текущей цепочке вложенных вызовов) в целях логирования ошибок. Но в современных браузерах существуют и другие способы это сделать.
Почему callee
и caller
устарели?
В современном стандарте эти свойства объявлены устаревшими, и использовать их не рекомендуется. Хотя де-факто они используются хотя бы потому, что IE до 9 версии не поддерживает Named Function Expression так, как должен.
Причина отказа от этих свойств простая — интерпретатор может оптимизировать JavaScript более эффективно. Тем более, что для arguments.callee
есть замена — NFE.
Важность: 5
Узнать количество реально переданных аргументов можно по значениюarguments.length
:
[Открыть задачу в новом окне]
Комментарии
Оставить комментарий
Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)
Термины: Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)