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.

brandonscript
  • 68,675
  • 32
  • 163
  • 220
  • 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" })` – lleaff Jan 14 '16 at 18:44
  • @lleaff this needs to be more flexible than that unfortunately – brandonscript Jan 14 '16 at 18:45

5 Answers5

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);
}
MinusFour
  • 13,913
  • 3
  • 30
  • 39
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];
}
zzzzBov
  • 174,988
  • 54
  • 320
  • 367
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.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
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

Poul Kruijt
  • 69,713
  • 12
  • 145
  • 149
  • 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)`. – oligofren Mar 07 '19 at 08:54
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);
Johan Karlsson
  • 6,419
  • 1
  • 19
  • 28
  • Yes, I'm aware of all of this - hence why I'm using the recommended approach from MDN of Array.prototype.slice.call() – brandonscript Jan 14 '16 at 19:24
  • 2
    @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. – zzzzBov Jan 14 '16 at 20:12
  • 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 Jan 16 '16 at 05:12
  • @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)` – oligofren Mar 07 '19 at 08:48