如何在JavaScript中将"arguments"对象转换为数组?

462
在JavaScript中,arguments对象是一个奇怪的点——它在大多数情况下表现得就像一个数组,但它实际上并不是一个真正的数组对象。由于它是完全不同的东西,因此它没有来自Array.prototype的有用函数,例如forEachsortfiltermap

使用简单的for循环可以轻松地从arguments对象构造一个新的数组。例如,这个函数对其参数进行排序:

function sortArgs() {
    var args = [];
    for (var i = 0; i < arguments.length; i++)
        args[i] = arguments[i];
    return args.sort();
}

然而,仅仅为了使用非常有用的JavaScript数组函数而进行这种操作是相当可悲的。是否有一种内置的方法可以使用标准库来实现?


我不喜欢这个问题的所有答案都认为有意图进行排序,而且“sort”已经渗透到了所有答案中,但是排序只是使用正确的数组可以做的事情之一。本意是要获取数组。我甚至不能责怪这个问题,因为它正确地说“例如”。然而,每个答案都决定将“sort”纳入将参数转换为数组的示例中 - 有时过于紧密地将排序与获取参数数组混为一谈。 - Wyck
21个回答

766

ES6使用剩余参数

如果您能够使用ES6,则可以使用:

剩余参数

function sortArgs(...args) {
  return args.sort(function (a, b) { return a - b; });
}

document.body.innerHTML = sortArgs(12, 4, 6, 8).toString();

正如您可以在链接中阅读的那样

剩余参数语法允许我们将无限数量的参数表示为数组。

如果您对...语法感到好奇,它被称为Spread Operator,您可以在这里阅读更多内容。

使用Array.from()的ES6

使用Array.from

function sortArgs() {
  return Array.from(arguments).sort(function (a, b) { return a - b; });
}

document.body.innerHTML = sortArgs(12, 4, 6, 8).toString();

Array.from 可以将类数组对象或可迭代对象转换为数组实例。



ES5

你实际上可以在一个参数对象上使用Arrayslice函数,它会将其转换为标准的JavaScript数组。你只需要通过Array的原型手动引用它即可:

function sortArgs() {
    var args = Array.prototype.slice.call(arguments);
    return args.sort();
}

这为什么有效呢?好吧,这里是ECMAScript 5文档的摘录:
注意:slice函数是有意通用的;它不要求其this值是一个Array对象。因此,它可以被转移到其他类型的对象以用作方法。无论slice函数能否成功应用于主机对象都取决于实现。因此,slice可在任何具有length属性的东西上工作,而arguments恰好具有该属性。

如果 Array.prototype.slice 对你来说太长了,你可以通过使用数组字面量来稍微缩短它:

var args = [].slice.call(arguments);

然而,我倾向于认为前一个版本更明确,所以我更喜欢它。滥用数组文字符号感觉不专业且看起来奇怪。

5
在最近的Firefox版本(2.0?)和ECMAScript 5中,有一个Array.slice方法可以使这个过程更加简单。你可以这样调用它:var arge = Array.slice(arguments, 0)。 - Matthew Crumley
9
“0”是干嘛用的?既然没有要传递的参数,为什么不能只使用“var args = Array.prototype.slice.call(arguments);”? MDC arguments page 建议不使用“0”。 - Peter Ajtai
3
@Barney - 这个排序是必要的,因为原问题需要对参数进行排序。如果你只想将其转换为一个数组,那么这个排序就不是必要的。 - Krease
17
你不应该在参数上进行切片,因为这会妨碍JavaScript引擎(例如V8)的优化。——来自MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments - mokaymakci
3
我不确定这是否回答了原始问题。时间过去了。随着在Firefox和Chrome中实验ES6,_rest参数_正在成为一个很好的替代方案 - function sortArgs(...argsToSort) { return argsToSort.sort(); } 来自MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters - Kir Kanos
显示剩余8条评论

40

值得一提的是,可以参考 这个 Bluebird promises 库的维基页面,该页面展示了如何以使函数能够在 V8 JavaScript 引擎下进行优化的方式将 arguments 对象转换成数组:

function doesntLeakArguments() {
    var args = new Array(arguments.length);
    for(var i = 0; i < args.length; ++i) {
        args[i] = arguments[i];
    }
    return args;
}

这种方法被用来替代var args = [].slice.call(arguments);。作者还展示了如何通过构建步骤来帮助减少冗余。


2
+1并感谢提供Bluebird优化杀手页面的链接。顺便说一句,我最近在node-thunkify项目中看到了这种优化的使用。 - Yves M.
对于在2021年阅读此内容的任何人,在链接页面上:“请不要将此处写的任何内容视为性能建议,这里仅出于历史原因。” - p0358

30
function sortArgs(){ return [].slice.call(arguments).sort() }

// Returns the arguments object itself
function sortArgs(){ return [].sort.call(arguments) }

一些数组方法故意不要求目标对象实际上是一个数组。它们只需要目标具有一个名为length的属性和索引(必须是零或更大的整数)。

[].sort.call({0:1, 1:0, length:2}) // => ({0:0, 1:1, length:2})

12

使用:

function sortArguments() {
  return arguments.length === 1 ? [arguments[0]] :
                 Array.apply(null, arguments).sort();
}

Array(arg1,arg2,...) 返回[arg1,arg2,...]

Array(str1) 返回[str1]

Array(num1) 返回一个包含num1个元素的数组

您必须检查参数的数量!

Array.slice 版本(较慢):

function sortArguments() {
  return Array.prototype.slice.call(arguments).sort();
}

Array.push版本(速度较慢,比slice快):

function sortArguments() {
  var args = [];
  Array.prototype.push.apply(args, arguments);
  return args.sort();
}

移动版(速度较慢,但大小较小则更快):

function sortArguments() {
  var args = [];
  for (var i = 0; i < arguments.length; ++i)
    args[i] = arguments[i];
  return args.sort();
}

Array.concat版本(最慢):

function sortArguments() {
  return Array.prototype.concat.apply([], arguments).sort();
}

1
请注意,由于Array.concat中的平铺行为,它将接受数组参数。sortArguments(2,[3,0],1)返回0,1,2,3。 - Hafthor

9

如果你正在使用jQuery,我认为以下内容更容易记忆:

function sortArgs(){
  return $.makeArray(arguments).sort();
}

1
这基本上就是我在纯JS中寻找的,但是+1是因为它很好地展示了jQuery如何使JS更易于理解。 - Andrew Coleson
2
除非还包括另一个框架/库的标签,否则应该提供纯JavaScript答案。 - Michał Perłakowski

9
在ECMAScript 6中,不需要使用像Array.prototype.slice()这样的丑陋技巧。相反,您可以使用扩展语法(...

(function() {
  console.log([...arguments]);
}(1, 2, 3))

这看起来很奇怪,但实际上很简单。它只是提取arguments的元素并将它们放回数组中。如果你还不理解,请参考以下示例:

console.log([1, ...[2, 3], 4]);
console.log([...[1, 2, 3]]);
console.log([...[...[...[1]]]]);

请注意,在一些老的浏览器中,如IE 11中它无法正常工作。因此,如果您想支持这些浏览器,应该使用Babel


5

这里是关于将参数转换为数组的几种方法的基准测试

对我来说,对于少量参数,最好的解决方案是:

function sortArgs (){
  var q = [];
  for (var k = 0, l = arguments.length; k < l; k++){
    q[k] = arguments[k];
  }
  return q.sort();
}

对于其他情况:
function sortArgs (){ return Array.apply(null, arguments).sort(); }

基准测试加1分。目前的图表显示,在Firefox浏览器中,您的“sortArgs”比“Array.apply”快6倍,但在最新版本的Chrome浏览器中慢两倍。 - joeytwiddle
1
由于Array.prototype.slice已被弃用,因为它会阻止JS V8引擎优化,我认为这是最好的解决方案,直到ES6得到广泛应用。+1 - Sasha
1
此外,像 Array.slice() 这样的数组泛型方法也已经被弃用。 - Sasha

5
这里有一个简洁明了的解决方案:

function argsToArray() {
  return Object.values(arguments);
}

// example usage
console.log(
  argsToArray(1, 2, 3, 4, 5)
  .map(arg => arg*11)
);

Object.values() 会将对象的值作为数组返回,而由于 arguments 是一个对象,它将把 arguments 转换为数组,从而提供了所有数组的辅助函数,如 mapforEachfilter 等。


4

我建议使用 ECMAScript 6 扩展运算符,这将把尾参数绑定到一个数组上。通过这种方法,您不需要操作 arguments 对象,您的代码将会更简洁。但是,这种解决方案的缺点是它不能在大多数浏览器中工作,因此您将需要使用 JS 编译器,例如 Babel。在底层,Babel 将使用 for 循环将 arguments 转换为数组。

function sortArgs(...args) {
  return args.sort();
}

如果您无法使用ECMAScript 6,我建议查看其他答案,例如@Jonathan Fingland。

function sortArgs() {
    var args = Array.prototype.slice.call(arguments);
    return args.sort();
}

4

Lodash:

var args = _.toArray(arguments);

实战演练:

(function(){ console.log(_.toArray(arguments).splice(1)); })(1, 2, 3)

生成:

[2,3]

6
为什么不使用_.toArray - Menixator
5
但是"_.toArray"更加合适。 - Menixator
我认为 _.slice 更合适,因为你经常想要删除一些前导参数。 - Hafthor

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