为什么JavaScript参数对象会因赋值给参数而发生变化?

Why are JavaScript Arguments objects mutated by assignment to parameter?

本文关键字:参数 变化 赋值 JavaScript 对象 为什么      更新时间:2023-09-26

这种行为背后的基本原理是什么?

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

也许这是一个真正的错误。ECMAScript规范的哪个部分定义了这种行为?

实际上,在严格模式下,这不会发生,正如你在这里看到的。

如果你阅读ECMA标准10.6节,特别是注释1,你会看到:

对于非严格模式函数,数组索引(在15.4中定义)命名参数对象的数据属性谁的数字名称值最初小于相应函数对象的形式参数的数量与函数执行上下文中相应的参数绑定共享它们的值。这意味着改变属性改变参数绑定的相应值,反之亦然。这种通信中断了这样的属性将被删除,然后重新定义,或者将该属性更改为访问器属性。对于严格模式函数中,参数对象的属性值只是传递给函数和的参数的副本属性值和形式参数值之间没有动态联系。

简而言之,这就是说,在非严格模式下,命名函数参数作为arguments对象中项的别名进行操作。因此,更改命名参数的值将更改等效arguments项的值,反之亦然。这不是一个错误。这是预期的行为。

作为一篇社论,依赖这种行为可能不是一个好主意,因为它可能导致一些非常混乱的代码。同样,这样的代码,如果在严格模式下执行,将不再工作。

x的变化反映在arguments[0]中,因为arguments的索引可能是匹配命名参数的getter/setter 。这在步骤11.c中定义。Ii of 10.6:

  1. 添加name作为列表元素mappedNames

  2. g是调用MakeArgGetter抽象操作的结果,参数为nameenv

  3. p是调用MakeArgSetter抽象操作的结果,参数为nameenv

  4. 调用map传递ToString(indx)的[[DefineOwnProperty]]内部方法,属性描述符{[[Set]]: p, [[Get]]: g, [[Configurable]]: true}和false作为参数。

在上面的步骤中注意到,这要求strictfalse,在这种情况下,调用f时为x提供一个值:
f()  // undefined, undefined (no argument, no getter/setter)
f(1) // 1, 42