445

The arguments object in JavaScript is an odd wart—it acts just like an array in most situations, but it's not actually an array object. Since it's really something else entirely, it doesn't have the useful functions from Array.prototype like forEach, sort, filter, and map.

It's trivially easy to construct a new array from an arguments object with a simple for loop. For example, this function sorts its arguments:

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

However, this is a rather pitiful thing to have to do simply to get access to the extremely useful JavaScript array functions. Is there a built-in way to do it using the standard library?

animuson
  • 50,765
  • 27
  • 132
  • 142
Andrew Coleson
  • 9,452
  • 7
  • 28
  • 30
  • 1
    closely related: [how does Array.prototype.slice.call() work?](http://stackoverflow.com/q/7056925/1048572) – Bergi Aug 05 '15 at 07:32

21 Answers21

740

ES6 using rest parameters

If you are able to use ES6 you can use:

Rest Parameters

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

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

As you can read in the link

The rest parameter syntax allows us to represent an indefinite number of arguments as an array.

If you are curious about the ... syntax, it is called Spread Operator and you can read more here.

ES6 using Array.from()

Using 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 simply convert Array-like or Iterable objects into Array instances.



ES5

You can actually just use Array's slice function on an arguments object, and it will convert it into a standard JavaScript array. You'll just have to reference it manually through Array's prototype:

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

Why does this work? Well, here's an excerpt from the ECMAScript 5 documentation itself:

NOTE: The slice function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the slice function can be applied successfully to a host object is implementation-dependent.

Therefore, slice works on anything that has a length property, which arguments conveniently does.


If Array.prototype.slice is too much of a mouthful for you, you can abbreviate it slightly by using array literals:

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

However, I tend to feel that the former version is more explicit, so I'd prefer it instead. Abusing the array literal notation feels hacky and looks strange.

ctrl-alt-delor
  • 6,726
  • 5
  • 30
  • 48
Jonathan Fingland
  • 53,185
  • 11
  • 81
  • 77
  • 5
    In recent Firefox versions (2.0?) and ECMAScript 5, there's an Array.slice method that make this a little simpler. You would call it like this: var arge = Array.slice(arguments, 0);. – Matthew Crumley Jun 07 '09 at 02:06
  • 2
    converting array-like objects to arrays via `slice()` unfortunately deson't work reliably across browsers - eg IE can't convert node lists this way; for a general function which works with all array-like objects, there's no way around manually looping over the entries – Christoph Jun 07 '09 at 15:13
  • 9
    What's the `0` for? Since there are no arguments you want to pass, why can't you just use `var args = Array.prototype.slice.call(arguments);` ? [ **The MDC arguments page** ](https://developer.mozilla.org/en/JavaScript/Reference/functions_and_function_scope/arguments) suggests the method without the `0`. – Peter Ajtai Oct 05 '10 at 17:56
  • @Christoph - IE should be able to convert `arguments`. This is because `arguments` has a properly set `length` property. As long as this is the case, the above method should work. (This works in IE 8: http://jsfiddle.net/6fyUT/ ) – Peter Ajtai Oct 05 '10 at 18:02
  • 1
    Why is the `sort` necessary? It's the one part that isn't self-explanatory (provided one understands the methods). In what circumstances would the one liner `return Array.prototype.slice.call(arguments, 0);` not produce the same results? – Barney May 22 '13 at 17:12
  • 3
    @Barney - the sort is necessary because the original question wants to sort the arguments. If you just want to convert it to an array, it's not necessary. – Krease Jul 05 '13 at 23:29
  • 1
    In short this can be: `[].slice.call(arguments)` – Koen. Mar 06 '14 at 10:50
  • For anybody that wants to know, you can easily append and prepend items into the `arguments` variable simply by using splice. E.G. `Array.prototype.splice.call(arguments,0,0,'new element');` (this will modify the arguments object without converting it to an array) – AlexMorley-Finch Mar 13 '14 at 15:19
  • 17
    "You should not slice on arguments because it prevents optimizations in JavaScript engines (V8 for example)." - from MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments – mokaymakci Dec 16 '14 at 22:01
  • 3
    I'm not sure that is answering to the original question. Time went by. With ES6 experimented in Firefox and Chrome, the _rest parameter_ is becoming a good alternative - `function sortArgs(...argsToSort) { return argsToSort.sort(); }` from MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters – Kir Kanos Mar 24 '15 at 14:39
  • The shortened way to do it has been beyond helpful for me. – munchschair Apr 15 '17 at 17:56
  • I don't see many real-world cases where not slicing arguments to improve perceived performance. – Andre Figueiredo Mar 24 '18 at 01:17
  • 1
    @frameworkninja What about `Array.from()` ? Does that also prevent optimizations ? – doubleOrt Sep 04 '18 at 09:33
40

It's also worth referencing this Bluebird promises library wiki page that shows how to manage the arguments object into array in a way that makes the function optimizable under V8 JavaScript engine:

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

This method is used in favor of var args = [].slice.call(arguments);. The author also shows how a build step can help reduce the verbosity.

Yves M.
  • 26,153
  • 20
  • 93
  • 125
jbmusso
  • 3,307
  • 1
  • 22
  • 35
  • 2
    +1 and thanks for the link to the Bluebird Optimization Killer page. PS: I've recently seen this optimization used in the [node-thunkify](https://github.com/tj/node-thunkify/commit/d537460eb23e3d556c4f726eb04bef189dd3994f) project. – Yves M. Jun 02 '15 at 18:04
29
function sortArgs(){ return [].slice.call(arguments).sort() }

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

Some array methods are intentionally made not to require the target object to be an actual array. They only require the target to have a property named length and indices (which must be zero or larger integers).

[].sort.call({0:1, 1:0, length:2}) // => ({0:0, 1:1, length:2})
animuson
  • 50,765
  • 27
  • 132
  • 142
matyr
  • 5,736
  • 26
  • 22
12

Use:

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

Array(arg1, arg2, ...) returns [arg1, arg2, ...]

Array(str1) returns [str1]

Array(num1) returns an array that has num1 elements

You must check number of arguments!

Array.slice version (slower):

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

Array.push version (slower, faster than slice):

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

Move version (slower, but small size is faster):

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

Array.concat version (slowest):

function sortArguments() {
  return Array.prototype.concat.apply([], arguments).sort();
}
  • 1
    Note that because of the flattening behavior in Array.concat, it will accept array arguments. sortArguments(2,[3,0],1) returns 0,1,2,3. – Hafthor Jan 26 '19 at 18:41
9

In ECMAScript 6 there's no need to use ugly hacks like Array.prototype.slice(). You can instead use spread syntax (...).

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

It may look strange, but it's fairly simple. It just extracts arguments' elements and put them back into the array. If you still don't understand, see this examples:

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

Note that it doesn't work in some older browsers like IE 11, so if you want to support these browsers, you should use Babel.

Michał Perłakowski
  • 70,955
  • 24
  • 137
  • 155
9

If you're using jQuery, the following is a good deal easier to remember in my opinion:

function sortArgs(){
  return $.makeArray(arguments).sort();
}
brad
  • 67,670
  • 21
  • 67
  • 84
5

Here is benchmark of several methods converting arguments into array.

As for me, the best solution for small amount of arguments is:

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

For other cases:

function sortArgs (){ return Array.apply(null, arguments).sort(); }
Gheljenor
  • 362
  • 3
  • 2
  • +1 for benchmarks. Currently the graphs show your `sortArgs` is 6 times faster in Firefox, but twice as slow in the latest Chrome, compared to `Array.apply`. – joeytwiddle Jul 18 '13 at 20:03
  • 1
    Since Array.prototype.slice is deprecated because it prevent JS V8 engine optimization I consider this the best solution until ES6 will be widely available +1 – Sasha Aug 24 '15 at 10:57
  • 1
    Moreover Array generics methods like Array.slice() are also deprecated – Sasha Aug 24 '15 at 10:59
4

I recommend using ECMAScript 6 spread operator, which will Bind trailing parameters to an array. With this solution you don't need to touch the arguments object and your code will be simplified. The downside of this solution is that it does not work across most browsers, so instead you will have to use a JS compiler such as Babel. Under the hood Babel transforms arguments into a Array with a for loop.

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

If you can not use a ECMAScript 6, I recommend looking at some of the other answers such as @Jonathan Fingland

function sortArgs() {
    var args = Array.prototype.slice.call(arguments);
    return args.sort();
}
jjbskir
  • 5,514
  • 4
  • 28
  • 44
4

Use Array.from(), which takes an array-like object (such as arguments) as argument and converts it to array:

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

Note that it doesn't work in some older browsers like IE 11, so if you want to support these browsers, you should use Babel.

Michał Perłakowski
  • 70,955
  • 24
  • 137
  • 155
4

Lodash:

var args = _.toArray(arguments);

in action:

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

produces:

[2,3]
Zane
  • 4,134
  • 1
  • 24
  • 26
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));
Abk
  • 1,669
  • 1
  • 17
  • 25
1

Another Answer.

Use Black Magic Spells:

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

Firefox, Chrome, Node.js, IE11 are OK.

  • 6
    This is definitely the best solution... if you want to make your code completely unreadable. Also, `__proto__` is deprecated. See [MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto). – Michał Perłakowski Jan 24 '16 at 16:52
1

Try using Object.setPrototypeOf()

Explanation: Set prototype of arguments to Array.prototype

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

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

Explanation: Take each index of arguments , place item into an array at corresponding index of array.

could alternatively use Array.prototype.map()

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

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

Explanation: Take each index of arguments , place item into an array at corresponding index of array.

for..of loop

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

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

or Object.create()

Explanation: Create object, set properties of object to items at each index of arguments; set prototype of created object to 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]]]))
guest271314
  • 1
  • 10
  • 82
  • 156
  • You've been [asking people](http://meta.stackoverflow.com/q/315961/1906307) whether your answer satisfies the warning under the question about "long answers". It seems to me the main issue with your answer is that you do not explain *why* one should use any of the methods you show here. There's no way I would use any of the solutions you propose because they are all worse than the solutions in the accepted answer. For instance, your reference to the doc of `Object.setPrototypeOf` warns that it is a "very slow operation". Why on earth would I use this over `Array.prototype.slice.call`? – Louis Feb 01 '16 at 19:53
  • @Louis Updated Answer with explanations for each approach. Question at OP appear to be _"Is there a built-in way to do it using the standard library?"_ , not _"why one should use any of the methods"_ that are built-in? How could any user that posted an Answer accurately determine if the posted solutions are "right" for OP ? It would seem that it is up to OP to determine this? Actually, this portion of notification is what interested this user: _"explain why your answer is right"_ . Do any of the Answers provided to this Question state: "This answer is "right" because: x,y,z"? – guest271314 Feb 01 '16 at 20:06
  • What I've explained is how SO generally works. Whether or not the OP wants to know why one solution is better than the other is totally irrelevant, because the questions posted for SO are not *primarily* for the OP but for the hundreds and thousand of people who will read the question and its answers later. Also, the people who vote on answers are not required to vote according to whatever the OP might be satisfied with. – Louis Feb 01 '16 at 20:12
1

Here's a clean and concise solution:

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

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

Object.values( ) will return the values of an object as an array, and since arguments is an object, it will essentially convert arguments into an array, thus providing you with all of an array's helper functions such as map, forEach, filter, etc.

Mystical
  • 1,805
  • 2
  • 15
  • 31
0

Benshmarck 3 methods :

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

Enjoy !

Matrix
  • 2,783
  • 6
  • 29
  • 59
  • 1
    Would like to suggest to modified to something like `arguments.length == 1?[arguments[0]]:Array.apply(null, arguments);` to prevent the function is called with only one integer argument `dummyFn(3)` and causes result `[,,]` – nevermind Jan 17 '16 at 18:00
  • @nevermind good job, I edit, your version is better ;) But I dont understand `dummyFn(3)` what is this? this function not exist... – Matrix Jan 21 '16 at 10:31
  • Uh... never mind, just a example want to show how a function called with only one integer argument and causes a fixed size empty array, sorry for my English. – nevermind Jan 22 '16 at 11:46
0

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

document.body.innerHTML = sortArgs(1, 2, 3, 4).toString();
wsc
  • 896
  • 7
  • 10
0

Although rest parameters work well, if you want to continue to use arguments for some reason, consider

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

[...arguments] can be considered a sort of alternative to Array.from(arguments), which also works perfectly well.

An ES7 alternative is an array comprehension:

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

This could be easiest if you want to process or filter the arguments prior to sorting:

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

I tried simple destructing technique

Gaurav
  • 763
  • 8
  • 10
0

The Arguments object is only available inside a function body. Although you can index the Arguments Object like an array, it is not an array. It does not have any array properties other than 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();

Although it can be indexed like an array as you can see in this example:

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

However, the Arguments object is not an array and does not have any other properties other than length.

You can convert the arguments object into an array at which point you can access the Arguments object.

There are many ways you can access the arguments object inside a function body, and these include:

  1. you can call the Array.prototoype.slice.call method.

Array.prototype.slice.call(arguments)

function giveMeArgs(arg1,arg2){

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

console.log( giveMeArgs(1,2));
  1. you can use the array literal

[].slice.call(arguments).

function giveMeArgs(arg1,arg2){

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

return args;

}

console.log( giveMeArgs(1,2) );
  1. you can use Rest ...

function giveMeArgs(...args){

return args;

}

console.log(giveMeArgs(1,2))
  1. you can use spread [...]

function giveMeArgs(){

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

}

console.log(giveMeArgs(1,2));
  1. you can use Array.from()

function giveMeArgs(){

var args = Array.from(arguments);

return args;

}

console.log(giveMeArgs(1,2));
Rick
  • 967
  • 9
  • 17
0

You can create a reusable function to do it with any arguments, the simplest one is something like this:

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

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

Spread syntax can be used in ES6 and above...

But if you'd like to use something compatible with ES5 and below, you can use Array.prototype.slice.call, so you code looks like this:

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

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

There are also few other ways to do this, for Example using Array.from or loop through the arguments and assign them to a new array...

Alireza
  • 83,698
  • 19
  • 241
  • 152
-2

This is a very old question, but I think I have a solution that is slightly easier to type than previous solutions and doesn't rely on external libraries:

function sortArguments() {
  return Array.apply(null, arguments).sort();
}
  • 2
    `Array.apply` won't work if you have only a single positive integer argument. It is because `new Array(3)` creates an array length of `3`. To be more specific the result will be `[undefined, undefined, undefined]` and not `[3]`. – inf3rno Aug 13 '14 at 01:01