41

Let's take a look at this code:

var mainFunction = function() {
  altFunction.apply(null, arguments);
}

The arguments that are passed to mainFunction are dynamic -- they can be 4 or 10, doesn't matter. However, I have to pass them through to altFunction AND I have to add an EXTRA argument to the argument list.

I have tried this:

var mainFunction = function() {
  var mainArguments = arguments;
  mainArguments[mainArguments.length] = 'extra data'; // not +1 since length returns "human" count.

  altFunction.apply(null, mainArguments);
}

But that does not seem to work. How can I do this?

4
  • What do you mean by not +1 since length returns "human" count? Commented Nov 28, 2012 at 17:32
  • .length returns 4 if the array is 0, 1, 2, 3 it doesn't return 3, hope you understand it now. That is what I meant :P Commented Nov 28, 2012 at 17:35
  • Well yeah, but that's the expected behavior of .length. When you use arr[arr.length] = "whatever", it's the same behavior as using .push(), and just adds the item to the end of the array. Your problem is that arguments is not an array, so that's why you need to use one of the many similar solutions that were provided. Commented Nov 28, 2012 at 17:37
  • Actually, you almost have it. Your code will work as is if you also do arguments.length++ before calling apply. Commented Nov 28, 2012 at 17:53

11 Answers 11

65

Use Array.prototype.push

[].push.call(arguments, "new value");

There's no need to shallow clone the arguments object because it and its .length are mutable.

(function() {
    console.log(arguments[arguments.length - 1]); // foo

    [].push.call(arguments, "bar");

    console.log(arguments[arguments.length - 1]); // bar
})("foo");

From ECMAScript 5, 10.6 Arguments Object

  1. Call the [[DefineOwnProperty]] internal method on obj passing "length", the Property Descriptor {[[Value]]: len, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}, and false as arguments.

So you can see that .length is writeable, so it will update with Array methods.

Sign up to request clarification or add additional context in comments.

5 Comments

Yeah, I could've sworn arguments was read-only, but then I re-read the MDN spec and realized this
[].splice.call(arguments,0,0,NEW_VAR) puts it at the first argument, very handy when you have a function that expects a certain value at first.
I think this is throwing an error in 'strict mode'. Use Array.prototype.push.call(arguments, 'bar') instead if you have problems.
I thought this was a hack but it's not. From the docs: "push is intentionally generic. This method can be used with call() or apply() on objects resembling arrays. The push method relies on a length property to determine where to start inserting the given values."
Strangely, simply assigning to arguments, like arguments[arguments.length] = "bar";, looks like it works, but then if you pass that arguments value to another function via apply(), the added arg is not passed to that function. It only receives the original arguments.
34

arguments is not a pure array. You need to make a normal array out of it:

var mainArguments = Array.prototype.slice.call(arguments);
mainArguments.push("extra data");

2 Comments

"You can't alter arguments array directly." Sure you can.
It should be noted that although you can modify the arguments like this, doing so prevents the js vm from being able to optimize the function call. So, if you're manipulating the arguments like this in a function that gets called a lot, there could be performance implications.
4

The arguments object isn't an array; it's like an array, but it's different. You can turn it into an array however:

var mainArguments = [].slice.call(arguments, 0);

Then you can push another value onto the end:

mainArguments.push("whatever");

Comments

4

Update 2016: You must convert the arguments to an array before adding the element. In addition to the slice method mentioned in many posts:

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

You can also use the Array.from() method or the spread operator to convert arguments to a real Array:

var args = Array.from(arguments);

or

var args = [...arguments];

The above may not be optimized by your javascript engine, it has been suggested by the MDN the following may be optimized:

var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments

Comments

2
var mainFunction = function() {
    var args = [].slice.call( arguments ); //Convert to array
    args.push( "extra data");
    return altFunction.apply( this, args );
}

Comments

2

The arguments "array" isn't an array (it's a design bug in JavaScript, according to Crockford), so you can't do that. You can turn it into an array, though:

var mainFunction = function() {
  var mainArguments = Array.prototype.slice.call(arguments);
  mainArguments.push('extra data');

  altFunction.apply(null, mainArguments);
}

5 Comments

Why is it a bug? Because you don't like it?
Because Douglas Crockford said so
@CallumMacrae: That's not much of a reason IMO.
Perhaps it's bad design, but that's not the same as a bug. IMO, a bug is when something does not work as intended. I assume arguments was made to be an Array-like object and not an Array on purpose, whether wisely or not.
To save memory, arguments prototype is lacking common array functionality, only the basics. Think for a moment, how many arrays are there? Lets guess, same amout as there are function calls? If you can save space, wouldn't you? If required, there is apply function to handle the case when arguments has been elaveted to full functioning array. Everything else is up to the user.
0

One liner to add additional argument(s) and return the new array:

[].slice.call(arguments).concat(['new value']));

Comments

0
//
//    var
//        altFn  = function () {}, 
//        mainFn = prefilled( altFn  /* ...params */ );
//
//    mainFn(  /* ...params */  );
//  
//        
function prefilled ( fn /* ...params */ ) { 
     return ( function ( args1 ) { 

          var orfn = this;

          return function () { 

              return orfn.apply( this, args1.concat( cslc( arguments ) ) );

          };

     } ).call( fn, cslc( arguments, 1 ) );
}

// helper fn
function cslc( args, i, j ) { 
   return Array.prototype.slice.call( args, i, j );
}


// example

var
    f1 = function () { console.log( cslc( arguments ) ); }, 
    F1 = prefilled( f1, 98, 99, 100 );

F1( 'a', 'b', 'c' );

//
//   logs: [98, 99, 100, "a", "b", "c"]
//
//

Comments

0

In this case it could be more comfortable to use call() instead of apply():

function first(parameter1, parameter2) {

  var parameter3 = "123";

  secondFunction.call(
    this,
    parameter1,
    parameter2,
    parameter3);
},

Comments

0

var myABC = '12321'; someFunction(result, error, myCallback.bind(this, myABC));

Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Comments

0

For those who like me was looking for a way to add an argument that is optional and may be not the only omitted one (myFunc = function(reqiured,optional_1,optional_2,targetOptional) with the call like myFunc(justThisOne)), this can be done as follows:

// first we make sure arguments is long enough
// argumentPosition is supposed to be 1,2,3... (4 in the example above)
while(arguments.length < argumentPosition)
    [].push.call(arguments,undefined);
// next we assign it
arguments[argumentPosition-1] = arguments[argumentPosition-1] || defaultValue;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.