如何在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个回答

4
使用 Array.from(),它接受一个类似数组的对象(例如 arguments)作为参数,并将其转换为数组:

(function() {
  console.log(Array.from(arguments));
}(1, 2, 3));

请注意,它不能在某些较旧的浏览器中工作,例如IE 11,因此如果您想支持这些浏览器,您应该使用Babel

3
function sortArg(){
var args = Array.from(arguments); return args.sort();
}

 function sortArg(){
    var args = Array.from(arguments); 
    return args.sort();
    }
 
 console.log(sortArg('a', 'b', 1, 2, '34', 88, 20, '19', 39, 'd', 'z', 'ak', 'bu', 90));


1
另一个答案。
使用黑魔法咒语:
function sortArguments() {
  arguments.__proto__ = Array.prototype;
  return arguments.slice().sort();
}

Firefox、Chrome、Node.js和IE11都可以。


6
如果您想使您的代码变得完全无法阅读,这绝对是最佳解决方案...此外,__proto__已被弃用。请参阅MDN文档 - Michał Perłakowski

1

尝试使用 Object.setPrototypeOf()

解释:将argumentsprototype设置为Array.prototype

function toArray() {
  return Object.setPrototypeOf(arguments, Array.prototype)
} 

console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))


arguments的每个索引项,放入数组中对应索引位置的数组中。可以选择使用Array.prototype.map()

function toArray() {
  return [].map.call(arguments, (_,k,a) => a[k])
} 

console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))


说明:取出arguments的每个索引,将项目放置到数组中对应的索引处。

for..of循环

function toArray() {
 let arr = []; for (let prop of arguments) arr.push(prop); return arr
} 

console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))


或者 Object.create() 解释:创建对象,将对象的属性设置为arguments中每个索引处的项;将创建的对象的prototype设置为Array.prototype

function toArray() {
  var obj = {};
  for (var prop in arguments) {
    obj[prop] = {
      value: arguments[prop],
      writable: true,
      enumerable: true,
      configurable: true
    }
  } 
  return Object.create(Array.prototype, obj);
}

console.log(toArray("abc", 123, {def: 456}, [0, [7, [14]]]))


你一直在询问他人是否满足关于“长答案”的问题下的警告。在我看来,你回答的主要问题是没有解释为什么应该使用你在这里展示的任何方法。我不会使用你提出的任何解决方案,因为它们都比被接受的答案中的解决方案更糟糕。例如,你提到Object.setPrototypeOf文档的参考警告说它是一个“非常慢的操作”。为什么我要使用它而不是Array.prototype.slice.call呢? - Louis
@Louis 更新了答案,并解释了每种方法的原理。OP的问题似乎是“是否有一种内置的方法可以使用标准库来完成?”,而不是“为什么要使用任何内置的方法”。任何发布答案的用户如何准确地确定所发布的解决方案是否适用于OP?这似乎取决于OP来确定?实际上,通知中的这部分内容是吸引该用户的原因:“解释为什么你的答案是正确的”。这个问题提供的任何答案都声明:“这个答案是“正确的”,因为:x,y,z”吗? - guest271314
我所解释的是SO一般的运作方式。无论OP是否想知道为什么一个解决方案比另一个更好都是完全无关紧要的,因为SO发布的问题不是主要为了OP,而是为了将来会阅读问题及其答案的数百或数千人。此外,投票回答的人不需要按照OP可能满意的任何方式进行投票。 - Louis

0

您可以创建一个可重复使用的函数来处理任何参数,最简单的函数可能是这样的:

function sortArgs() {
  return [...arguments].sort();
}

sortArgs('ali', 'reza', 1, 2, 'a'); //[1, 2, "a", "ali", "reza"];

展开语法可以在ES6及以上版本中使用...

但是,如果您想使用兼容ES5及以下版本的语法,可以使用Array.prototype.slice.call,这样您的代码就会像这样:

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

sortArgs('ali', 'reza', 1, 2, 'a'); //[1, 2, "a", "ali", "reza"];

还有其他几种方法可以实现这个,例如使用 Array.from 或循环遍历 arguments 并将它们赋值给一个新数组...


0

尽管剩余参数很好用,但如果您因某种原因想继续使用arguments,请考虑

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

[...arguments] 可以被视为 Array.from(arguments) 的一种替代方式,它同样可以完美地工作。

ES7 的另一种替代方式是数组推导式:

[for (i of arguments) i].sort()

这可能是最简单的方法,如果您想在排序之前处理或筛选参数的话。
[for (i of arguments) if (i % 2) Math.log(i)].sort()

0
 function x(){
   var rest = [...arguments]; console.log(rest);return     
   rest.constructor;
 };
 x(1,2,3)

我尝试了简单的解构技术


0

三种Benshmarck方法:

function test()
{
  console.log(arguments.length + ' Argument(s)');

  var i = 0;
  var loop = 1000000;
  var t = Date.now();
  while(i < loop)
  {
      Array.prototype.slice.call(arguments, 0); 
      i++;
  }
  console.log(Date.now() - t);


  i = 0,
  t = Date.now();
  while(i < loop)
  {
      Array.apply(null, arguments);
      i++;
  }
  console.log(Date.now() - t);

  i = 0,
  t = Date.now();
  while(i < loop)
  {
      arguments.length == 1 ? [arguments[0]] : Array.apply(null, arguments);
      i++;
  }
  console.log(Date.now() - t);
}

test();
test(42);
test(42, 44);
test(42, 44, 88, 64, 10, 64, 700, 15615156, 4654, 9);
test(42, 'truc', 44, '47', 454, 88, 64, '@ehuehe', 10, 64, 700, 15615156, 4654, 9,97,4,94,56,8,456,156,1,456,867,5,152489,74,5,48479,89,897,894,894,8989,489,489,4,489,488989,498498);

RESULT?

0 Argument(s)
256
329
332
1 Argument(s)
307
418
4
2 Argument(s)
375
364
367
10 Argument(s)
962
601
604
40 Argument(s)
3095
1264
1260

尽情享受吧!


1
建议修改为类似于 arguments.length == 1?[arguments[0]]:Array.apply(null, arguments); 的形式,以防止函数仅被调用一次整数参数 dummyFn(3) 并导致结果为 [,,] - nevermind
@nevermind 干得好,我编辑了一下,你的版本更好 ;) 但是我不明白 dummyFn(3) 是什么?这个函数不存在... - Matrix
呃...算了,只是想展示一个函数如何只带一个整数参数并导致一个固定大小的空数组,抱歉我的英语。 - nevermind

0

Arguments对象仅在函数体内可用。虽然您可以像数组一样索引Arguments对象,但它不是数组。除了length之外,它没有任何数组属性。

// function arguments length 5
function properties(a,b,c,d,e){
var function_name= arguments.callee.name
var arguments_length= arguments.length;
var properties_length=  properties.length; 
var function_from= properties.caller.name;
console.log('I am the function name: '+ function_name);
console.log('I am the function length, I am function spacific: '+ properties_length); 
console.log('I am the arguments length, I am context/excution spacific: '+ arguments_length);
console.log('I am being called From: '+  function_from );
}

// arguments 3
function parent(){
properties(1,2,3);
}

//arguments length 3 because execution spacific
parent();

虽然它可以像数组一样进行索引,就像您在这个例子中看到的那样:

function add(){

var sum=0;

for(var i=0; i< arguments.length;i++){

sum = sum + arguments[i];

}

return sum;

}

console.log(add(1,2,3));

然而,Arguments对象不是数组,并且除了长度之外没有任何其他属性。

您可以将arguments对象转换为数组,此时您可以访问Arguments对象。

有许多方法可以在函数体内访问参数对象,包括:

  1. 您可以调用Array.prototoype.slice.call方法。

Array.prototype.slice.call(arguments)

function giveMeArgs(arg1,arg2){

var args = Array.prototype.slice.call(arguments);
return args
}

console.log( giveMeArgs(1,2));

  1. 你可以使用数组字面量

[].slice.call(arguments).

function giveMeArgs(arg1,arg2){

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

return args;

}

console.log( giveMeArgs(1,2) );

  1. 你可以使用Rest...

function giveMeArgs(...args){

return args;

}

console.log(giveMeArgs(1,2))

你可以使用展开操作符(spread operator)来 [...]

function giveMeArgs(){

var args = [...arguments];
return args;

}

console.log(giveMeArgs(1,2));

你可以使用 Array.from()。

function giveMeArgs(){

var args = Array.from(arguments);

return args;

}

console.log(giveMeArgs(1,2));


0

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

document.body.innerHTML = sortArgs(1, 2, 3, 4).toString();


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