为什么arguments是类数组对象

为什么JavaScript里函数的arguments只是array-like object?

只是标准这么规定的,还是有什么设计原因在里面?

JavaScript的函数里面的arguments对象有 .lengh 属性和可以通过 [] 访问,但是确实从Object.prototype继承的。很多时候都会用 Array.prototype.slice.call(arguments) 或者 Array.from(arguments) 转成数组。直接设计成数组不是更好吗?

 

 

这个问题又激起了我的八卦之心!

在一番寻找之后,找到了似乎是目前网上仅存的 Brendan Eich 本人探讨 arguments 的资料,是一份录音: (来自 aminutewithbrendan.com 这个已经被关闭的网站,似乎是和当时的赞助事件调查有关所以被关掉了,但是 archive.org 有存档哈哈)

大致上,BE 本人也承认 arguments 的设计是因为当时只花了十天所以整得太糙了。在正式规范化 JavaScript 的时候,Microsoft 曾经有人提出把 arguments 改成真正的 Array,BE 本人甚至都打算动手改实现了,但是 MS 那边回去商量了下又回来觉得多一事不如少一事,不改了。于是这个糟糕的设计就从此成为了规范... 这是 1997 年的第一版 ES 规范。

除了 arguments.callee 之外,还有一个神奇的 quirk,那就是 arguments 和实际的参数变量之间的迷之绑定。规范里是这么说的:

In the case when iarg is less than the number of formal parameters for the function object, this property shares its value with the corresponding property of the activation object. This means that changing this property changes the corresponding property of the activation object and vice versa. The value sharing mechanism depends on the implementation.

换言之,假设一个函数的第一个参数是 a,当你修改 a 的值的时候,arguments[0] 也会同步变化:

(function (a) {
  console.log(arguments[0] === a) // -> true
  console.log(a) // -> 1
     
  // 修改 arguments
  arguments[0] = 10
  console.log(a) // -> 10
 
  // 修改参数变量
  a = 20
  console.log(arguments[0]) // -> 20
})(1,2)

后面的事情你也知道了,ES 规范是要向后兼容的,而且上面的这个 quirk 使得它在引擎实现中需要很多特殊处理,一旦改动,兼容性影响巨大,所以它永远也改不了了。据说在 ES5 讨论时也有人提出要把 arguments 改成 Array 的 subclass,但是很快就不了了之,只是在 strict mode 下对 arguments.callee 和上面的绑定 quirk 进行了限制。直到 ES6 终于对 arguments 提供了一个替代品 - rest parameters:

function foo (...args) {
  // 这里 args 终于是真正的 Array 了!
}

BE 本人并没有提到为什么一开始会把 arguments 设计成对象,因此我们也只能做猜测。但一个合理的推测是,ES1 里面的 Array.prototype 其实很弱,只有四个方法:toString, join, reverse 和 sort - 连 push, pop, shift, unshift, splice 都没有!而 forEach, filter, map, reduce 这些有用的方法更是 ES5 才添加进来的。所以当时 arguments 就算真的继承自 Array 貌似也没什么大用,所以就这样被放过了... 当然,这只是我们的猜测,估计 BE 自己今天也说不清自己当时为什么这么干的了吧。



原文地址:https://www.cnblogs.com/oklfx/p/8127473.html