为什么在JavaScript中函数的参数对象不是一个数组?

55

因为似乎人们做的第一件事就是将arguments转换为真正的数组,所以我很想知道JavaScript语言作者和实现者为什么决定并继续认为arguments不应该成为一个真正的Array。我并不是说这是挑衅,我真诚地对此背后的思考感兴趣。由于该函数在其主体内部自然被调用,我认为这并不是因为对象 arguments 正在引用的内容可能会更改,就像某些DOM结果一样...


1
这在ES6中不是问题。您可以使用rest参数,它是一个真正的数组。 - Dan Dascalescu
4个回答

53

我的猜测:

arguments 对象的概念从语言一开始就存在了,甚至在 ECMAScript 第一版标准(PDF) 中都有描述。

在那个版本的 ECMAScript 中,Array.prototype 很基本,数组对象只包含 4 个方法!toStringjoinreversesort

我认为这是它们让 arguments 继承自 Object.prototype 的主要原因之一,当时这些数组方法看起来并不太 有用

但是在标准的下一个版本中,Array.prototype 对象得到了扩展,现在在 ES5 中,数组对象具有诸如 mapreduceeverysome非常强大的 方法。

去年,ES5 中有一个提议,让 arguments 继承自 Array.prototype,在标准的草案阶段,但后来被放弃了。

在那些草案中,arguments 继承自 Array.prototype,但为了向后兼容 ES3,arguments 对象定义了两个自有属性,toStringtoLocaleString,都指向 Object.prototype 上的相同方法,但最终委员会决定继续从 Object.prototype 继承。


18
猜测?看起来你好像在所有委员会会议上都出席了.. 哈哈 - Anurag
5
谁在场都知道这件事:http://www.slideshare.net/douglascrockford/newandimproved,顺便说一句,总结得不错,+1。但这并没有告诉你为什么委员会“继续认为`arguments`不应该是一个真正的`Array`”。 - gblazex
2
@galambalazs:我认为委员会决定的原因是担心破坏网络,ES5标准经过精心设计,避免了任何激进的改变,事实上没有引入任何新的语法到该语言中。我所谈论的提案被放弃,因为他们讨论了极端边缘情况的不兼容性,例如重新定义Object.prototype。也许将来会有所改变... - Christian C. Salvadó
1
@CMS:更改其原型会破坏网站,这不是推测,而是已知事实。Opera在将近两年的时间内使用Array.prototype进行发布。 - gsnedders

35

arguments对象有一个非常不寻常的特点,即其类似数组的元素是函数参数的本地变量的同义词。例如:

function f(x) {
   console.log(arguments[0]);   // Displays the initial value of the argument x
   x = 5;                       // Changes the value of the local variable x
   console.log(arguments[0]);   // Now displays 5
}

我一直认为这种"神奇的行为"是 arguments 不是数组的原因。


正确,但我也可以有function a() { console.log(arguments) }; a(1, 2, 3);... - pr1001
是的,这种行为仅适用于具有命名参数的情况。 - Daniel Vassallo
11
幸运的是,在ES5严格模式下,这种“linkage”已被移除了 :)(我不喜欢魔法!)。 - Christian C. Salvadó
@CMS:我也不知道 :)... 你认为这是 arguments 没有被实现为数组的原因吗? - Daniel Vassallo
1
@Daniel,不是不可变的,arguments 对象本身是可以改变的(无论严格模式下的各种语义限制如何)。唯一的变化是它的 [[Class]] 内部属性包含字符串 "Arguments",例如:Object.prototype.toString.call(arguments) == "[object Arguments]"; - Christian C. Salvadó
显示剩余3条评论

6
重要的是要注意,如果没有设计师在场,我们只能猜测为什么。但我们可以想出一些不错的理由...这是我的理由:
从功能的角度来看,其中一个原因可能是因为显然无法更改传递给您的参数。您可以更改表示传递给您的参数的数组,但在执行范围之前,作为传递的参数已经定型。
您可以对数组进行拼接、切片和弹出操作,如果对arguments对象进行这样的操作,则会破坏概念上不可变的结构(伤心!)。真正的arguments对象的设计更接近JavaScript可以提供的一种不可变性。
这类似于查询字符串参数。客户端发送请求时向您提供了一个集合。它是请求信息的一部分,已经设置并完成。

5
我不确定我是否完全同意这里的推理。 arguments 只是一个对象,虽然我们无法在技术上更改实际的参数,但我们可以通过数组索引 - arguments [0]arguments [1] 等来对整个 arguments 对象或其表示的各个参数进行任何操作。为什么它不是一个 Array,或者给定一个类似数组的接口仍然值得考虑。同样的问题也适用于 NodeList。 - Anurag
@Anurag 我并不完全不同意...就像我说的,我们只能猜测为什么,这是我的理论 :) - Rex M
1
好的观点,我认为ES5中创建防篡改对象的选项是一个很好的步骤,而arguments可以充分利用它。我能想到的一个关于基本接口的原因是,ES5委员会在进行重大更改时基本上负责整个Web,所以不幸的是变化缓慢且困难。 - Anurag

3

arguments不仅仅返回参数。它还会返回调用对象以及参数数组。如果只返回一个数组,第一个元素可能是调用对象,从而更加混淆不清。


3
也许问题应该是,为什么没有单独的“callee”对象?为什么它应该是“arguments”的属性? - pr1001

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接