使用参数数组更改 JavaScript 函数的参数值不起作用

Changing JavaScript function's parameter value using arguments array not working

我正在学习 JavaScript 并且对 arguments 属性 数组感到很困惑。

我有一个接受单个参数的函数,returns 它。当我传递参数并使用 arguments[0] = value 重新分配它时,它正在更新值。

function a(b) {
  arguments[0] = 2;
  return b;
}
console.log(a(1)); //returns 2

但是当我不带参数调用同一个函数时 returns undefined.

function a(b) {
  arguments[0] = 2;
  return b;
}
console.log(a()); //returns undefined

但即使我通过 undefined,该值也会更新。

function a(b) {
  arguments[0] = 2;
  return b;
}
console.log(a(undefined)); //returns 2

我想如果你不给JavaScript函数传递一个参数,它会自动创建它并将值赋给undefined,更新后它应该反映更新后的值,对吧?

还有a()a(undefined)是一回事吧?

ECMA 262 9.0 2018 describes this behaviour in 9.4.4 Arguments Exotic Objects

NOTE 1:

The integer-indexed data properties of an arguments exotic object whose numeric name values are less than the number of formal parameters of the corresponding function object initially share their values with the corresponding argument bindings in the function's execution context. This means that changing the property changes the corresponding value of the argument binding and vice-versa. This correspondence is broken if such a property is deleted and then redefined or if the property is changed into an accessor property. If the arguments object is an ordinary object, the values of its properties are simply a copy of the arguments passed to the function and there is no dynamic linkage between the property values and the formal parameter values.

简而言之,

  • 如果在'sloppy mode'中,则所有参数都映射到它们的命名变量,如果长度对应于给定的参数,或者

  • 如果在'strict mode'中,则在交出参数后绑定丢失。

这只能在具有

ECMA 262 7.0 2016. It describes this behaviour in 9.4.4 Arguments Exotic Objects 的旧版本中读取

Note 1:

For non-strict functions the integer indexed data properties of an arguments object whose numeric name values are less than the number of formal parameters of the corresponding function object initially share their values with the corresponding argument bindings in the function's execution context. This means that changing the property changes the corresponding value of the argument binding and vice-versa. This correspondence is broken if such a property is deleted and then redefined or if the property is changed into an accessor property. For strict mode functions, the values of the arguments object's properties are simply a copy of the arguments passed to the function and there is no dynamic linkage between the property values and the formal parameter values.

这是因为 arguments 它不像数组,它是一个具有整数索引数据键和 属性 长度的对象,如果长度等于零则意味着你没有参数

function a(b) {
    arguments[0] = 2;
    console.log(arguments.length) 
    return b;
}
a(1); // length 1  returns 2
console.log(a());  // length 0  returns undefined

我的理解是参数对象只跟踪传递给函数的内容。由于您最初没有传递任何内容,因此 b 未绑定,此时 arguments 不是 'tracking' b。接下来,您将一个值分配给初始化但空的 Array-like 对象 arguments,最后是 return b,它是未定义的。

进一步研究:

If a non-strict function does not contain rest, default, or destructured parameters, then the values in the arguments object do change in sync with the values of the argument variables. See the code below:

function func(a) { 
  arguments[0] = 99; // updating arguments[0] also updates a
  console.log(a);
}
func(10); // 99

function func(a) { 
  a = 99; // updating a also updates arguments[0]
  console.log(arguments[0]);
}
func(10); // 99

When a non-strict function does contain rest, default, or destructured parameters, then the values in the arguments object do not track the values of the arguments. Instead, they reflect the arguments provided when the function was called:

function func(a = 55) { 
  arguments[0] = 99; // updating arguments[0] does not also update a
  console.log(a);
}
func(10); // 10

function func(a = 55) { 
  a = 99; // updating a does not also update arguments[0]
  console.log(arguments[0]);
}
func(10); // 10

// An untracked default parameter
function func(a = 55) { 
  console.log(arguments[0]);
}
func(); // undefined

来源:MDN Web docs

当您未提供任何参数时,arguments 数组的 length 等于 0。然后您尝试将数组中不存在的元素设置为 2,这会导致返回未定义

您可以使用以下代码段简单地进行测试:

function a(b){ 
    alert(arguments.length) // It will prompt 0 when calling a() and 1 when calling a(undefined)
    arguments[0] = 2; 
    return b; 
}

这是 javascript 规范中未定义的值定义:

变量未赋值时使用的原始值。

因此,如果您不指定函数 return 类型,它将 return 未定义。

所以 a() 和 a(undefined) 不是一回事。 return未定义是基于return类型是否定义。

更多说明

分配给 arguments 索引只会更改关联的参数值(我们称它为第 n 个参数)如果函数被调用时至少有 n 个参数。 arguments 对象的数字索引属性本质上是 setters(和 getters):

http://es5.github.io/#x10.6

下面的斜体字是我对这个过程如何与问题相关的评论:

(Let) args (be) the actual arguments passed to the [[Call]] internal method

  1. Let len be the number of elements in args.

  2. Let indx = len - 1.

  3. Repeat while indx >= 0, (so, the below loop will not run when no arguments are passed to the function:)

(assign to the arguments object being created, here called map:)

    1. Add name as an element of the list mappedNames.
    1. Let g be the result of calling the MakeArgGetter abstract operation with arguments name and env.
    1. Let p be the result of calling the MakeArgSetter abstract operation with arguments name and env.
    1. Call the [[DefineOwnProperty]] internal method of map passing ToString(indx), the Property Descriptor {[[Set]]: p, [[Get]]: g, [[Configurable]]: true}, and false as arguments.

因此,如果调用函数时不带参数,arguments[0] 上将不会有 setter,因此重新分配它不会更改索引 0 处的参数。

同样的事情也发生在其他指标上——如果你用一个参数调用一个函数,但是这个函数接受两个参数,分配给 arguments[1] 不会改变第二个参数,因为 arguments[1] 没有 setter:

function fn(a, b) {
  arguments[1] = 'bar';
  console.log(b);
}
fn('foo');

所以

a() and a(undefined) are the same thing right?

不是这种情况,因为第二个导致 arguments 对象在索引 0 上具有 setter 和 getter,而第一个不是。

请注意,arguments 和函数参数之间的这种奇怪的交互仅存在于草率模式中。在严格模式下,对 arguments 的更改不会对单个参数标识符包含的值产生任何影响:

'use strict';
function a(b) {
  arguments[0] = 2;
  return b;
}
console.log(a(1)); //returns 1