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

Posted

技术标签:

【中文标题】使用参数数组更改 JavaScript 函数的参数值不起作用【英文标题】:Changing JavaScript function's parameter value using arguments array not working 【发布时间】:2019-06-16 19:09:06 【问题描述】:

我正在学习 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) 也是同一个东西,对吧?

【问题讨论】:

【参考方案1】:

如果函数至少使用n 参数调用,则分配给arguments 索引只会更改关联的参数值(我们称之为n-th 参数)。 arguments 对象的数字索引属性本质上是 setter(和 getter):

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

下面的斜体是我关于该过程如何与问题相关的cmets:

(让)args(是)传递给 [[Call]] 内部方法的实际参数

    len 为args 中的元素个数。

    indx = len - 1

    Repeat while indx >= 0,所以,当没有参数传递给函数时,下面的循环将不会运行:

(分配给正在创建的参数对象,这里称为map:)

    name 添加为mappedNames 列表的元素。
    g 成为使用参数nameenv 调用MakeArgGetter 抽象操作的结果。
    p 成为使用参数nameenv 调用MakeArgSetter 抽象操作的结果。
    调用map的[[DefineOwnProperty]]内部方法传递ToString(indx),属性描述符[[Set]]:p,[[Get]]:g,[[可配置]]:truefalse 作为参数。

因此,如果在没有参数的情况下调用该函数,arguments[0] 上将没有设置器,因此重新分配它不会更改索引 0 处的参数。

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

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

fn('foo');

所以

a()a(undefined) 是一回事吗?

情况并非如此,因为第二个结果是一个arguments 对象,它在索引 0 上有一个 setter 和一个 getter,而第一个没有。

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

'use strict';
function a(b) 
  arguments[0] = 2;
  return b;

console.log(a(1)); //returns 1

【讨论】:

因此,如果我理解正确,那么如果我不传递任何参数,那么 arguments 对象的长度将为 0。因此,如果我尝试使用 arguments[0] 访问该长度为 0 的对象,它应该不会抛出一个错误?还是好像有一个索引 0 但没有 setter 和 getter? arguments 对象是无论如何定义的,几乎任何对象都可以随意分配任何属性。例如,const foo = ; foo[0] = 'bar';。这有点类似于您的arguments[0] = 2a 被调用时没有参数 - 索引 0 上没有 setter 或 getter,因此您可以毫无问题地分配给它,它不会抛出错误,但它不会不会影响其他任何东西。 我明白了。谢谢。哇 JavaScript 很奇怪。 很好,好久没有看到引用 ES5 规范的答案了 :-) 不过这里完全合适。 @AmitDas 总是"use strict" mode,JavaScript 变得不那么奇怪了。【参考方案2】:

ECMA 262 9.0 20189.4.4 Arguments Exotic Objects 中描述了这种行为

注 1:

参数外来对象的整数索引数据属性,其数字名称值小于相应function object 的形式参数的数量,最初与函数execution context 中的相应参数绑定共享它们的值。这意味着更改属性会更改参数绑定的相应值,反之亦然。如果此类属性被删除然后重新定义,或者如果该属性更改为accessor property,则此对应关系将被破坏。如果 arguments 对象是普通对象,则其属性值只是传递给函数的参数的副本,并且属性值和形参值之间没有动态链接。

总之,

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

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

这只能在旧版本的ECMA 262 7.0 2016 中读取。它在9.4.4 Arguments Exotic Objects 中描述了这种行为

注1:

对于非严格函数,其数字名称值小于相应函数对象的形式参数数量的参数对象的整数索引数据属性最初与函数执行上下文中的相应参数绑定共享它们的值。这意味着更改属性会更改参数绑定的相应值,反之亦然。如果此类属性被删除然后重新定义,或者如果该属性更改为访问器属性,则该对应关系将被破坏。对于严格模式函数,arguments 对象的属性值只是传递给函数的参数的副本,属性值和形参值之间没有动态链接。

【讨论】:

【参考方案3】:

我的理解是 arguments 对象只跟踪传递给函数的内容。由于您最初没有通过任何东西,b 没有被绑定,此时arguments 不是“跟踪”b。接下来,为已初始化但为空的类数组对象 arguments 赋值,最后返回未定义的 b。

进一步深入研究:

如果非严格函数不包含剩余、默认或解构参数,则参数对象中的值会与参数变量的值同步更改。请看下面的代码:

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

当非严格函数确实包含剩余、默认或解构参数时,参数对象中的值不会跟踪参数的值。相反,它们反映了调用函数时提供的参数:

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 不是未定义的 - 它的长度仅等于 0 @puffy.bun 你说得对,我已经更新了答案【参考方案4】:

这是来自 javascript 规范的未定义值定义:

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

所以如果你不指定函数返回类型,它将返回 undefined。

所以 a() 和 a(undefined) 它不是一回事。返回 undefined 取决于返回类型是否定义。

更多说明similar_problem

【讨论】:

这显然是参数对象的错误,具体取决于模式。【参考方案5】:

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

你可以用这个 sn-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; 

【讨论】:

【参考方案6】:

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

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

【讨论】:

以上是关于使用参数数组更改 JavaScript 函数的参数值不起作用的主要内容,如果未能解决你的问题,请参考以下文章

在 JavaScript 中将数组作为函数参数传递

Rails 3 升级:使用数组作为参数从 js.erb 调用 javascript 函数

javascript 函数作为参数名

JavaScript,当变量作为参数传递时更改函数内部的变量值[重复]

JavaScript 神奇的参数

35.JavaScript对象和数组的解构赋值基础详解let陷阱函数参数解构