4

I'm writing several functions that take different types of parameters. As such, it's much simpler to just do:

var myFunc = function() {
    var args = Array.prototype.slice.call(arguments)
}

...than to try to handle all the different possibilities directly in the parameter field of the function declaration, e.g.:

var myFunc = function(param1, param2, param3) {
    if (param3) {
        // etc.
    }
}

Array.prototype.slice.call(arguments) adds a lot to the code though, and reduces readability. I'm trying to figure out if there's a way to write a helper function that could accomplish the same thing as Array.prototype.slice.call(), but cleaner and easier to read. I was trying something like:

var parseArgs = function() {
    return Array.prototype.slice.call(arguments)
}

var foo = function() {
    console.log(parseArgs(arguments))
}

foo('one', 'two', 'three')
// [Arguments, ['one', 'two', 'three']]

But obviously that doesn't work.

2
  • Not sure about your exact use case but maybe the option object pattern is a better choice, e.g.: function foo(o) { log(o.name) } foo({ name: "Bar" }) Commented Jan 14, 2016 at 18:44
  • @lleaff this needs to be more flexible than that unfortunately 😉 Commented Jan 14, 2016 at 18:45

5 Answers 5

5

If you don't want to write Array.prototype.slice.call(args) everytime you can do:

var slice = Function.call.bind(Array.prototype.slice);

And then you can use it anywhere like:

function fn(){
    var arr = slice(arguments);
}
Sign up to request clarification or add additional context in comments.

5 Comments

I'm using Node.js though, so while this would work in a single script, I can't see it working with module.exports()?
It should work on Node too. Unless you do something weird with their globals.
Depends I guess where I load the module where it's implemented. If I do that before I load any other modules that depend on it, it would probably work.
@remus This also will work, because this doesn't use any browser/node specific feature :-)
Oh, yeah I realize it's not node-specific.
3

A few shorthands for converting array-like objects to actual arrays in JS:

Alias Array.prototype.slice:

(function (slice) {
  ...your code...
  function someFunction() {
    var args = slice.call(arguments);
    ...
  }
  ...
}(Array.prototype.slice));

Use [].slice:

function someFunction() {
  var args = [].slice.call(arguments);
}

Create your own slice (you tried this but had an error in the arguments):

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

Use the modern Array.from (be sure to polyfill for backwards compatibility):

function someFunction() {
  var args = Array.from(arguments);
}

If you're in an environment where you can safely use the spread operator, then there is no need for any call to slice:

function someFunction() {
  var args = [...arguments];
}

1 Comment

Or, var args = [...arguments];.
2

You need to pass the arguments object of foo and slice only that, like this

function parseArgs(args) {
    return Array.prototype.slice.call(args);
}

When you use arguments inside parseArgs it will refer to the arguments received by that function only not the one which foo received.


In ECMA Script 2015, you can use Array.from on arguments object directly, like this

function foo() {
    console.log(Array.from(arguments));
}

Alternatively, you can keep the parseArgs as it is and spread the arguments over parseArgs in ECMA Script 2015, like this

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

function foo() {
    console.log(parseArgs(...arguments));
}

This basically means that you are spreading the arguments received by foo and passing them over to parseArgs so that it will also receive the same arguments.

Comments

1

What about:

var parseArgs = function(args) {
    return Array.prototype.slice.call(args)
}

var foo = function() {
    console.log(parseArgs(arguments))
}

foo('one', 'two', 'three')

This way you parse the actual arguments array

1 Comment

Alternatively, you could cache slice to avoid the performance hit of indirection on every call: var slice = Array.prototype.slice;. Or just bind everything directly once: var parseArgs = Function.call.bind(Array.prototype.slice).
1

There are numerous ways to use arguments in a way that causes the function to be unoptimizable. One must be extremely careful when using arguments.

From arguments object on MDN:

Important: You should not slice on arguments because it prevents optimizations in JavaScript engines (V8 for example). Instead, try constructing a new array by iterating through the arguments object.

As Node.js uses V8 you probably want to do something like this:

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

function myFunc() {
  var args = parseArgs.apply(null, arguments);
  console.log(args); // => [1,2,3]
}

myFunc(1, 2, 3);

4 Comments

Yes, I'm aware of all of this - hence why I'm using the recommended approach from MDN of Array.prototype.slice.call()
@remus, MDN recommends against using Array.prototype.slice.call for performance optimization reasons. Of course, it's rarely likely that Array.prototype.slice.call(arguments) is a bottleneck, so I wouldn't really worry much about this particular issue.
It's interesting, the MDN warning is a bit ambiguous; it sounds more like they're saying you shouldn't use arguments.slice() directly. I suppose I misinterpreted it.
@brandonscript There is no arguments.slice, so you cannot call it directly. slice is an array method, which arguments is not, which is the whole reason why you are converting arguments to an array in the first place. So it's not ambiguous at all. Just try this in your console: (function (){ return arguments.slice(); })(1,2,3)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.