Variable length argument lists

JavaScript functions can accept variable length argument lists. By taking advantage of this powerful feature, developers have greater control over how functions are defined and invoked. This post will discuss:

  • How to insert variable number of arguments into a function
  • How JavaScript can implement function overloading
  • The length property of argument lists

First let’s look at how the apply() method can be used in this situation. Suppose you need to find out the largest or smallest number in an Array. JavaScript has no built in function that can perform this simple task. Even though the Math object has min() and max() methods neither one accepts an Array as the argument list. That’s amazing but true. Fortunately there is a work around for this problem shown in the code below:

function minimum(array){
   return Math.min.apply(Math, array);
}

function maximum (array) {
   return Math.max.apply(Math, array);
}

myArray = [3, 6, 7, 12, 2, 8];

if(minimum(myArray) == 2){
   alert("Found the smallest number");
}

if(maximum(myArray) == 12){
   alert("Found the largest number");
}

run the code

Function overloading

The key to variable arguments list is the arguments parameter, which all JavaScript functions possess.
It provides access to all passed arguments in a function even if not all have been defined when the function was created.
In other object oriented languages like Java, method overloading is implemented by defining separate methods for each possible argument list. However in JavaScript, there is only one function definition which can then be invoked with various argument lists.
The code sample below illustrates how to merge the properties of several objects into one root object. The initial function definition has one argument, this corresponds to the {name: "Titanic"} object at initialization. After looping through the parameters list, the object becomes {name: "Titanic", port: "Pier 24", company: "Disney"}

  function merge(root){
	for (var i = 1; i < arguments.length; i++){
		for (var key in arguments[i]) {
			root[key] = arguments[i][key];
		}
	}
	return root;
}

var merged = merge (
	{name: "Titanic"},
	{port: "Pearl Harbour"},
	{company: "Disney"}

);

if(merged.name =="Titanic"){alert("The name is still the same")};
if(merged.port == "Pearl Harbour") {alert("The port has been moved over")};
if(merged.company == "Disney") {alert("The company has also been moved over")};

run the code

You can find examples in JavaScript libraries such as jQuery, the animate() function below accepts one object argument in the first case and two in the second:

// animate() takes one object, an integer and a function as arguments
 $('#port').animate({
   opacity: 0.25,
   left: '+=50',
   height: 'toggle'
   }, 5000, function() {
   // Animation complete.
   });

// here animate() takes two objects and a method as arguments
 $('#port').animate({
   width: 'toggle',
   height: 'toggle'
   }, {
   duration: 5000,
   specialEasing: {
     width: 'linear',
     height: 'easeOutBounce'
   },
   complete: function() {
     $(this).after('<div>The Easing Animation is complete.</div>');
   }
});

Functions also have a length property, similar to the arguments parameter’s length property. The function length property has a value equal to the number of items in the arguments list when the function is invoked. Whereas the arguments length property is the number of items when the function is defined. These two values may be equal except when you have function overloading.

The code sample below illustrates a simple function overloading example. Here we three methods with the same name – find bound to a base object. The three methods accept different number of arguments. The first accepts no argument, while the second accepts one argument, and the third accepts two arguments.

One important fact is that these methods are referenced from within closures. A closure is created when a function is able to access variables that are defined outside its normal scope. This topic will be discussed further in the next post.

Even though function overloading is a powerful technique, this example has some fundamental weaknesses. It only works for functions with different arguments length, and does not differentiate between argument type or name. Since the functions all have the same name, there’s a function call overhead that could affect performance.

run the code

   	function addMethod(object, name, fn){
	var old = object[name];

	object[name] = function(){
		if(fn.length == arguments.length){
			return fn.apply(this, arguments)}
			else if (typeof old == 'function')
			return old.apply(this, arguments);
		};
}

var ninjas = {
	values: ["Abraham Lincoln", "George Washington", "Charles Drew"]
};

addMethod(ninjas, "find", function(){
	return this.values;
});

addMethod(ninjas, "find", function(name){
	var ret =[];
	for(var i = 0; i < this.values.length; i++)
	if(this.values[i].indexOf(name) == 0)
	ret.push(this.values[i]);
	return ret;
});

addMethod(ninjas, "find" , function(first, last){
	var ret =[];
	for  (var  i = 0; i <this.values.length; i++)
		if (this.values[i] == (first + " " + last))
		ret.push(this.values[i])
		return ret;
});

if(ninjas.find().length == 3){ alert("Found all Ninjas")};

if(ninjas.find("George").length == 1){alert("Found Ninja by first name")};

if(ninjas.find("Abraham", "Lincoln").length == 1){alert("Found Ninja by first  and last name")};