Перейти к содержанию

Объект arguments

В области видимости любой функции в JavaScript есть доступ к специальной переменной arguments. Эта переменная содержит в себе список всех аргументов, переданных данной функции.

Замечание: В случае, если переменная arguments уже была объявлена в области видимости функции либо путём присвоения через выражение var, либо являясь формальным параметром, объект arguments не будет создан.

Объект arguments не является наследником Array. Он, конечно же, очень похож на массив и даже содержит свойство length — но он не наследует Array.prototype, а представляет собой Object.

По этой причине, у объекта arguments отсутствуют стандартные методы массивов, такие как push, pop или slice. Хотя итерация с использованием обычного цикла for по аргументам работает вполне корректно, вам придётся конвертировать этот объект в настоящий массив типа Array, чтобы применять к нему стандартные методы массивов.

Конвертация в массив

Указанный код вернёт новый массив типа Array, содержащий все элементы объекта arguments.

Array.prototype.slice.call(arguments)

Эта конвертация занимает много времени и использовать её в критических частях кода не рекомендуется.

Передача аргументов

Ниже представлен рекомендуемый способ передачи аргументов из одной функции в другую.

function foo() {
  bar.apply(null, arguments)
}
function bar(a, b, c) {
  // делаем здесь что-нибудь
}

Другой трюк — использовать и call и apply вместе, чтобы быстро создать несвязанную обёртку:

function Foo() {}

Foo.prototype.method = function (a, b, c) {
  console.log(this, a, b, c)
}

// Создаём несвязанную версию "method"
// Она принимает параметры: this, arg1, arg2...argN
Foo.method = function () {
  // Результат: Foo.prototype.method.call(this, arg1, arg2... argN)
  Function.call.apply(Foo.prototype.method, arguments)
}

Формальные аргументы и индексы аргументов

Объект arguments создаёт по геттеру и сеттеру и для всех своих свойств и для формальных параметров функции.

В результате, изменение формального параметра также изменит значение соответствующего свойства объекта arguments и наоборот.

function foo(a, b, c) {
  arguments[0] = 2
  a // 2

  b = 4
  arguments[1] // 4

  var d = c
  d = 9
  c // 3
}
foo(1, 2, 3)

Мифы и правда о производительности

Объект arguments создаётся во всех случаях, лишь за двумя исключениями — когда он переопределён внутри функции (по имени) или когда одним из её параметров является переменная с таким именем. Неважно, используется при этом сам объект или нет.

Геттеры и сеттеры создаются всегда; так что их использование практически никак не влияет на производительность.

ES5 Замечание: Эти геттеры и сеттеры не создаются в strict-режиме.

Однако, есть один момент, который может радикально понизить производительность современных движков JavaScript. Этот момент — использование arguments.callee.

function foo() {
  arguments.callee // сделать что-либо с этим объектом функции
  arguments.callee.caller // и с вызвавшим его объектом функции
}

function bigLoop() {
  for (var i = 0; i < 100000; i++) {
    foo() // При обычных условиях должна бы была быть развёрнута...
  }
}

В коде выше, функция foo не может быть развёрнута (а могла бы), потому что для корректной работы ей необходима ссылка и на себя и на вызвавший её объект. Это не только кладёт на лопатки механизм развёртывания, но и нарушает принцип инкапсуляции, поскольку функция становится зависима от конкретного контекста вызова.

Крайне не рекомендуется использовать arguments.callee или какое-либо из его свойств. Никогда.

ES5 Замечание: В strict-режиме использование arguments.callee породит TypeError, поскольку его использование принято устаревшим.