如何在JavaScript中将“参数”对象转换为数组?


433

argumentsJavaScript中的对象是一个奇怪的疣-在大多数情况下,其行为就像一个数组,但实际上不是数组对象。因为它是真正的完全是另一回事,它没有从有用的功能Array.prototype类似forEachsortfilter,和map

使用一个简单的for循环从arguments对象构造新数组非常容易。例如,此函数对其参数进行排序:

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

但是,仅获得对极其有用的JavaScript数组函数的访问权限,这是一件相当可怜的事情。是否有使用标准库的内置方法?


Answers:


725

ES6使用剩余参数

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

休息参数

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

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

如您在链接中所读

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

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

ES6使用Array.from()

使用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 只需将类似Array或Iterable的对象转换为Array实例。



ES5

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

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

为什么这样做?好吧,这是ECMAScript 5文档本身的摘录:

注意:该slice函数是有意通用的;它不需要其值是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);。
马修·克鲁姆利

9
有什么0用 由于没有要传递的参数,为什么不使用var args = Array.prototype.slice.call(arguments);?[ MDC参数页面 ](developer.mozilla.org/en/JavaScript/Reference/…)建议了不带的方法0
彼得·阿杰泰

3
@Barney-排序是必需的,因为原始问题要对参数进行排序。如果只想将其转换为数组,则没有必要。
Krease

17
“您不应切入参数,因为它会阻止JavaScript引擎(例如V8)中的优化。” -从MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
frameworkninja

3
我不确定这是否在回答原始问题。时间流逝。随着ES6在Firefox和Chrome实验中,其余参数正在成为一个不错的选择- function sortArgs(...argsToSort) { return argsToSort.sort(); }从MDN developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
基尔Kanos

40

还值得参考这个Bluebird Promise库Wiki页面,该页面显示了如何以arguments使函数在V8 JavaScript引擎下可优化的方式将对象管理为数组:

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,感谢您指向“蓝鸟优化杀手”页面的链接。PS:我最近看到了在node-thunkify项目中使用的这种优化。
伊夫·

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

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

有意使某些数组方法不要求目标对象是实际数组。它们仅要求目标具有名为length和indexs(必须为零或更大的整数)的属性。

[].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 版本(较慢,比切片更快):

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 09年

2
“除非还包括用于框架/库的另一个标签,否则期望纯JavaScript答案。”
米哈尔Perłakowski

6

在ECMAScript 6中,无需使用丑陋的hack 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。目前的图表显示sortArgs,与相比,您在Firefox中的速度快6倍,但在最新的Chrome中速度是您的两倍Array.apply
joeytwiddle13年

1
由于Array.prototype.slice已被弃用,因为它阻止了JS V8引擎优化,因此我认为这是最好的解决方案,直到ES6广泛可用+1
Sasha

1
此外,不赞成使用Array.slice()之类的Array泛型方法
Sasha

4

我建议使用ECMAScript 6 Spread运算符,该运算符会将尾随参数绑定到数组。使用此解决方案,您无需触摸arguments对象,您的代码将得到简化。该解决方案的缺点是它不适用于大多数浏览器,因此您将不得不使用JS编译器(例如Babel)。在后台,Babel转换arguments为带有for循环的Array。

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

4

Use 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文档
米哈尔Perłakowski

1

尝试使用 Object.setPrototypeOf()

说明:设置prototypeargumentsArray.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更新了答案,并提供了每种方法的说明。OP的问题似乎是“是否有使用标准库的内置方法?” ,而不是内置的“为什么应该使用任何一种方法”?发布答案的用户如何准确确定发布的解决方案是否适合OP?似乎要由OP来确定这一点?实际上,通知的这一部分正是该用户感兴趣的:“解释您的答案正确的原因”。是否为此问题提供了任何答案,因为“ x,y,z”是“正确”?
guest271314 '02

我所解释的是SO一般如何工作。OP是否想知道为什么一个解决方案比另一个解决方案更好是完全无关紧要的,因为为SO发布的问题主要不是针对OP,而是针对成千上万将在以后阅读该问题及其答案的人。同样,对答案投票的人也不必根据OP可能满意的内容进行投票。
路易

1

这是一个简洁明了的解决方案:

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,等。


0

Benshmarck 3种方法:

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);

结果?

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干得好,我编辑,您的版本更好;)但是我不明白dummyFn(3)这是什么?此功能不存在...
矩阵

嗯...没关系,仅举一个例子来说明一个函数如何仅用一个整数参数调用并导致固定大小的空数组,对不起我的英语。
没关系

0

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

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


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

Arguments对象仅在函数体内可用。尽管您可以像数组一样索引Arguments对象,但它不是数组。除长度外,它没有任何其他数组属性。

// 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对象不是数组,并且除了length之外没有任何其他属性。

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

您可以通过多种方式访问​​函数体内的arguments对象,其中包括:

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

Array.prototype.slice.call(参数)

function giveMeArgs(arg1,arg2){

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

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

  1. 您可以使用数组文字

[] .slice.call(参数)。

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))

  1. 您可以使用传播[...]

function giveMeArgs(){

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

}

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

  1. 您可以使用Array.from()

function giveMeArgs(){

var args = Array.from(arguments);

return args;

}

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


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或循环遍历参数并将它们分配给新数组...


-2

这是一个非常老的问题,但是我认为我有一个比以前的解决方案更易于键入且不依赖外部库的解决方案:

function sortArguments() {
  return Array.apply(null, arguments).sort();
}

2
Array.apply如果您只有一个正整数参数,将无法使用。这是因为new Array(3)创建的数组长度为3。更具体地说,结果将是[undefined, undefined, undefined]而不是[3]
inf3rno
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.