Function arguments

  1. Accessing the unnamed arguments
  2. The guts of arguments
  3. Using arguments as Array
  4. Making a real Array
  5. Default values
  6. Keyword arguments
  7. Special arguments properties
    1. arguments.callee
    2. arguments.callee.caller

In JavaScript, a function may be called with any number of arguments, no matter how many of them are listed.

For instance:

function go(a,b) {
  alert("a="+a+", b="+b)
}

go(1)     // a=1, b=undefined
go(1,2)   // a=1, b=2
go(1,2,3) // a=1, b=2, extra argument is not listed

Arguments which are not provided become undefined. So we can see if the function is called with no arguments:

function check(x) {
  alert(x === undefined) // ok now I know there is no x
}

check()

No syntax-level polymorphism

In some languages, a programmer may write two functions with same name but different parameter list, and the interpreter/compiler would choose the right one:

function log(a) {
  ...
}

function log(a,b,c) {
  ...
}

log(a) // first function is called
log(a,b,c) // second function is called

That is called function polymorphism. In JavaScript, there’s no such thing.

There can be only one function named log, which is called for any given arguments.

Accessing the unnamed arguments

How do we get more arguments than listed in parameters?

There is a special pseudo-array inside each function called arguments.

It contains all parameters by their number: arguments[0], arguments[1] etc.

Here is an example of how to list all arguments, no matter how many of them:

function sayHi() {
  for(var i=0; i<arguments.length; i++) {
    alert("Hi, " + arguments[i])
  }
}
 
sayHi("Cat", "Alice")  // 'Hi, Cat', then 'Hi, Alice'

All parameters are in arguments, even if the function names some of them: sayHi(a,b,c).

The guts of arguments

A frequent beginner mistake is to use Array methods on it. Shortly, you can’t:

function sayHi() {
  var a = arguments.shift() // error! arguments is not Array
  alert(a)
}

sayHi()

If it isn’t Array, then what it is? Let’s use the [[Class]] property to see it:

(function() {

  alert( {}.toString.call(arguments) )  // [object Arguments] 

})()

This type is internally almost same as Object. It only looks like array, because it’s keys are numeric and it has length, but here the similarity ends.

Using arguments as Array

There is still a way to call array methods on arguments:

function sayHi() {
  var args = [].join.call(arguments, ':')
  alert(args)  // 1:2:3
}

sayHi(1,2,3)

Here we execute the join method of Array in the context of arguments, providing ':' as first argument.

It works, because internally most array methods (and join) use numeric indexes and length to work.

The join would work on a custom object as well, if the format is correct:

var obj = { 0: "A", 1: "B", length: 2 }

alert( [].join.call(obj) ) // "A,B"

Making a real Array

The arr.slice(start, end) copies the part of arr from start to end into the new array.

It can be called in the context of arguments to copy all elements into a real array:

function sayHi() {
  var args = [].slice.call(arguments) // slice without parameters copies all

  alert( args.join(':') ) // now .join works
}

sayHi(1,2)

Gotcha! `arguments` are parameters

The arguments and named parameters reference same values.

Updating arguments[..] causes the corresponding parameter to change and vice versa. For example:

f(1)

function f(x) {
  arguments[0] = 5
  alert(x) // 5, updating arguments changed x
}

And the reverse way:

f(1)

function f(x) {
  x = 5
  alert(arguments[0]) // 5, update of x reflected in arguments
}

Array methods which modify arguments also modify local parameters:

sayHi(1)

function sayHi(x) {
  alert(x)           // 1
  [].shift.call(arguments)  
  alert(x)           // undefined, no x any more :/  
}

Actually, the modern ECMA-262 5th specification splits arguments from local variables. But as of now, browsers still behave like described above. Try the examples to see.

Generally, it is a good practice not to modify arguments.

Default values

If you want a function parameter to be optional, there are two ways.

  1. First, you could check if for undefined and reassign:
    function sayHi(who) {
      if (who === undefined) who = 'me' 
        
      alert(who)  
    }
    
  2. Or, use the OR || operator:

    function sayHi(who) {
      who = who || 'me'  // if who is falsy, returns 'me'
        
      alert(who)  
    }
    sayHi("John")
    sayHi()  // 'me'
    

    This way works great for parameters which are either not given or true. It happens most of time in real life.

A well-known function with variable number of arguments is Math.max. It returns the greatest or it’s arguments:

alert( Math.max(1, -2, 7, 3) )

Use func.apply(context, args) and Math.max to find the greatest element of an array:

var arr = [1, -2, 7, 3]
/* your code to output the greatest value of arr */

Open solution
Solution

The solution:

var arr = [1, -2, 7, 3]

alert( Math.max.apply(Math, arr) ) // (*)

Here, we call Math.max, passing array of arguments args.

In the “natural” call Math.max(...), the context this is set to Math, the object before dot '.'. In our code we keep it same by explicitly passing to apply.

Actually, talking about Math.max - this method does not use the context at all. We could abuse that to simplify our code even further:

var arr = [1, -2, 7, 3]

alert( Math.max.apply(0, arr) ) // dummy '0' context

Keyword arguments

Imagine you’ve got a function with several arguments. And most of them will have default values.

Like the following:

function showWarning(width, height, title, contents, showYesNo) {
  width = width || 200; // default values
  height = height || 100;
  
  var title = title || "Warning";

  ...
}

Here we have a function to show a warning window. It allows to specify width, height, title, textual contents and show the button if showYesNo is set.

Most parameters have default values.

Here is how we use it:

showWarning(null, null, null, "Warning text", true)
// or
showWarning(200, 300, null, "Warning text")

The problem is: people tend to forget arguments order, and what they mean.

Imagine you have 10 arguments and most are optional. The function call becomes really terrifying.

The technique of keyword arguments exists in Python, Ruby and many other languages.

In JavaScript, it is implemented with a parameters object:

function showWarning(options) {
  var width = options.width || 200  // defaults 
  var height = options.height || 100
  
  var title = options.title || "Warning"

  // ...
}

Calling such function is easy. You just pass an object of arguments like this:

showWarning({ 
  contents: "Text", 
  showYesNo: true
})

Another bonus is that the arguments object can be reconfigured and reused:

var opts = {
  width: 400,
  height: 200, 
  contents: "Text", 
  showYesNo: true
}

showWarning(opts)

opts.contents = "Another text"
showWarning(opts) // another text with same options

Keyword arguments are employed in most frameworks.

Special arguments properties

arguments.callee

There is an interesting property of arguments, namely arguments.callee. It references the function which is being run.

This property is deprecated by ECMA-262 in favor of named function expressions and for better performance.

JavaScript implementations can optimize the code much better if they know that keeping arguments.callee is not required.

It will trigger error in “strict mode”, which you need to enable. Normally, it works.

Usually it is used for recursion in anonymous functions.

For example, setTimeout(func, ms) is a built-in function which calls func after ms microseconds.

setTimeout(  
  function() { alert(1) }, // alerts 1 after 1000 ms (=1 second)
  1000
)

The function has no name. To call it recursively 3 times, let’s use arguments.callee:

var i = 1
setTimeout(  
  function() { 
    alert(i) 
    if (i++<3) setTimeout(arguments.callee, 1000)
  }, 
  1000
)

Another example is factorial:

// factorial(n) = n*factorial(n-1)
var func = function(n) {  
  return n==1 ? 1 : n*arguments.callee(n-1)  
}

The factorial function given above works even if func is reassigned to something else. That’s because it uses arguments.callee to reference itself.

arguments.callee.caller

The property arguments.callee.caller keeps the reference to a calling function.

This property is deprecated by ECMA-262, for the same reason as arguments.caller.

There is a property arguments.caller with same meaning, but less supported. Don’t use it, stick to arguments.callee.caller, all browsers have it.

In the example below, arguments.callee.caller references the calling function of g, that is f.

f()

function f() {
  alert(arguments.callee.caller) // undefined
  g()
}

function g() {
  alert(arguments.callee.caller) // f
}

Tutorial

Donate

Donate to this project