我正在学习JavaScript,并对arguments
属性数组非常困惑。
我有一个接受单个参数并返回它的函数。当我传递参数并使用arguments[0] = value
重新分配参数时,它将更新值。
function a(b) {
arguments[0] = 2;
return b;
}
console.log(a(1)); //returns 2
但是当我调用不带参数的相同函数时,它将返回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)
也是同一件事,对吧?
答案 0 :(得分:23)
如果使用至少arguments
个参数调用该函数,则分配给n
的索引仅会更改关联的参数值(将其称为第n
个参数)。 arguments
对象的数字索引属性本质上是 setters (和getter):
下面的斜体是我对流程与问题的关系的评论:
(让我们)
args
(是)传递给[[Call]]内部方法的实际参数
让
len
为args中的元素数。让
indx
=len - 1
。在
indx >= 0
期间重复(因此,当没有参数传递给函数时,以下循环将不会运行:)(分配给正在创建的参数对象,这里称为
map
:)
- 将
name
添加为列表mappedNames
的元素。
- 让
g
是用参数MakeArgGetter
和name
调用env
抽象操作的结果。
- 让
p
是用参数MakeArgSetter
和name
调用env
抽象操作的结果。
- 调用
map
的[[DefineOwnProperty]]内部方法,并传递ToString({indx
),属性描述符{[[Set]]:p
,[[Get]]:g
,[[Configurable]]:true
}和false
作为参数。
因此,如果不带任何参数调用该函数,则arguments[0]
上将没有设置方法,因此重新分配它不会更改索引0处的参数。
其他指标也发生同样的事情-如果您调用带有1个参数的函数,但是该函数接受两个参数,则分配给arguments[1]
不会更改第二个参数,因为{{1} }没有设置器:
arguments[1]
所以
function fn(a, b) { arguments[1] = 'bar'; console.log(b); } fn('foo');
和a()
是对的吗?
并非如此,因为第二个结果生成的a(undefined)
对象在索引0上有一个setter和一个getter,而第一个则没有。
答案 1 :(得分:6)
ECMA 262 9.0 2018在9.4.4 Arguments Exotic Objects中用
描述了此行为注意1:
外来参数的实参索引数据属性,该外来对象的数字名称值小于相应的function object的形式参数的数量,最初与函数{{3}中的相应实参绑定共享它们的值}。这意味着更改属性会更改参数绑定的相应值,反之亦然。如果删除此类属性然后重新定义,或者将该属性更改为execution context,则此对应关系将被破坏。如果arguments对象是一个普通对象,则其属性值只是传递给该函数的参数的副本,并且属性值和形式参数值之间没有动态链接。
简而言之,
如果在'sloppy mode'
中,那么如果长度与给定参数相对应,则所有参数都映射到其命名变量,或者
如果在'strict mode'
中,则在传递参数后绑定将丢失。
仅在旧版本的accessor property中可读。它在ECMA 262 7.0 2016中用
描述了此行为注1:
对于非严格函数,其数字名称值小于相应功能对象的形式参数数量的参数对象的整数索引数据属性最初在函数的执行上下文中与相应的参数绑定共享其值。这意味着更改属性会更改参数绑定的相应值,反之亦然。如果删除此类属性然后重新定义,或者将该属性更改为访问器属性,则此对应关系将被破坏。对于严格模式函数,arguments对象的属性值只是传递给该函数的参数的副本,并且属性值和形式参数值之间没有动态链接。
答案 2 :(得分:1)
我的理解是arguments对象仅跟踪传递给函数的内容。由于您最初没有传递任何内容,因此b
没有绑定,并且此时arguments
尚未“跟踪” b
。接下来,您为已初始化但为空的类似数组的对象arguments
分配一个值,最后返回未定义的b。
进一步研究:
如果非严格函数不包含rest,default或destructed参数,则arguments对象中的值的确会与参数变量的值同步变化。请参见下面的代码:
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
当非严格函数确实包含rest,默认值或重构参数时,arguments对象中的值将不会跟踪参数的值。相反,它们反映了调用函数时提供的参数:
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
答案 3 :(得分:1)
这是javascript规范中的未定义值定义:
未为变量分配值时使用的原始值。
因此,如果您未指定函数返回类型,则它将返回未定义。
所以a()和a(undefined)是不一样的。返回undefined取决于是否定义了返回类型。
更多说明similar_problem
答案 4 :(得分:0)
这是因为参数与数组不同,它是一个具有整数索引数据键和属性长度的对象,并且如果长度等于零,则意味着您没有参数
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
答案 5 :(得分:0)
当不提供任何参数时,arguments
数组的length
等于0。然后您尝试将不存在的数组元素设置为2
,这将导致返回未定义的< / p>
您可以使用以下代码片段对此进行简单测试:
function a(b){
alert(arguments.length) // It will prompt 0 when calling a() and 1 when calling a(undefined)
arguments[0] = 2;
return b;
}