Почему объекты JavaScript Arguments, мутированные путем присвоения параметру?

В чем причина такого поведения?

function f(x) {
  console.log(arguments[0]);
  x = 42;
  console.log(arguments[0]);
}

f(1);
// => 1
// => 42

Возможно, это была настоящая ошибка. Какой раздел спецификации ECMAScript определяет это поведение?

Ответ 1

Собственно, в строгом режиме это не происходит, как которое вы можете увидеть здесь.

Если вы прочитали раздел 10.6 стандарта ECMA, в частности Примечание 1, вы увидите:

Для функций нестрого режима индекс массива (определенный в 15.4) назвал свойства данных объекта аргументов числовые значения которых меньше числа формальных параметров соответствующего функционального объекта делиться своими значениями с соответствующими привязками аргументов в контексте выполнения функции. Это означает, что изменение свойство изменяет соответствующее значение привязки аргумента и наоборот. Это соответствие нарушается, если такое свойство удаляется, а затем переопределяется или изменяется свойство в свойство accessor. Для строгого режима функции, значения свойств объекта arguments являются просто копией аргументов, переданных функции, и динамическая связь между значениями свойств и формальными значениями параметров отсутствует.

Короче говоря, это говорит о том, что в нестрогом режиме названные функциональные параметры работают как псевдонимы для элементов в объекте arguments. Таким образом, изменение значения именованного параметра изменит значение эквивалентного элемента arguments и наоборот. Это не ошибка. Это ожидаемое поведение.

Как редакционная статья, вероятно, не стоит полагаться на это поведение, так как это может привести к очень запутанному коду. Кроме того, такой код, если он выполняется в строгом режиме, больше не будет работать.

Ответ 2

Изменение x отражается в arguments[0], потому что индексы arguments могут быть getter/seters для сопоставления именованного аргумента. Это определяется в шаге 11.c.ii из 10.6:

  • Добавить имя как элемент списка mappedNames.

  • Пусть g - результат вызова абстрактной операции MakeArgGetter с именами аргументов и env.

  • Пусть p является результатом вызова абстрактной операции MakeArgSetter с именами аргументов и env.

  • Вызвать внутренний метод [[DefineOwnProperty]] для передачи карты ToString (indx), дескриптор свойства {[[Set]]: p, [[Get]]: g, [[Configurable]]: true} и false в качестве аргументов.

Как указано выше, для этого требуется, чтобы Строгий был false, и в этом случае вызывается f со значением для x

f()  // undefined, undefined (no argument, no getter/setter)
f(1) // 1, 42