1024px-Flatirons_Winter_Sunrise_edit_2

Enterprise developers have a tendency to misunderstand JavaScript’s function parameters. When getting started, many expect JavaScript functions to work much like the server-side languages they’re already familiar with. There can then be a fair bit of confusion when they find out things work a little differently with JavaScript.

The languages generally used on the server-side of enterprise development have static typing. The number and type of each parameter must be declared. In contrast JavaScript is considered a language with dynamic typing. JavaScript parameters do not have to be tied to a specific type at compile time. Additionally server-side languages do not include any of the ‘free’ parameters we get in JavaScript (more discussion on these parameters below).

[cta id=”3166″ align=”none”]

A great example of the gap between common server-side languages and JavaScript is the .each method. To use this method you supply a function which will be executed against each element within the jQuery wrapped collection.

My first encounter with .each was similar to this:

[js] //…
// note: this example is archaic as of 1.4 release
// please use function callback with .text instead!
$(‘.someClass’).each(function() {
var $this = $(this);
$this.text($this.text() + " some extra text");
});
//…
$(‘.someClass’).each(function(idx,val) {
if(!(idx % 3)) { //modulus : will be true
//on every third element
$(val).somejQueryMethod();
}
});
//…
[/js] JS Bin demo


In my first encounter with this code I stared with a combination of wonder and horror. How are there two anonymous functions that each have different number of parameters? How could jQuery possibly invoke each of these functions successfully?

The secret isn’t jQuery’s. These snippets work because of the way JavaScript handles parameters.

In this post we are going to take a look at some of the language semantics for functions parameters. We’ll then use that knowledge to come up with a parameter passing pattern that is incredibly useful and expressive.

Javascript Function Parameters

There is an incredible amount of freedom in parameter passing in JavaScript. This flexibility comes from a few key concepts regarding JavaScript function parameters:

Declaring and passing parameters is optional in JavaScript

I could declare a function with five named parameters but the caller of that function does not have to pass in any parameters. Conversely I could declare a function with zero named parameters and the caller could pass in five when invoking the function.

[js] var myFunction1 = function(param1, param2, param3) {
// …
};

myFunction1();
// myFunction1 will execute when invoke, even though
// there is a parameter mistmatch

var myFunction2 = function() {
// …
};

myFunction2("here", "are", "some", "parameters");
// myFunction2 will execute when invoked even though there
// are no named parameters in the function declaration
[/js] JS Bin Demo

Every function includes an arguments array-like object as a parameter

You actually never have to declare parameters when declaring a function in JavaScript. Arguments passed in during function invocation will all be stored in a variable with the keyword arguments. arguments is an array-like object which you can use to retrieve and use all parameters in the order they were passed in. You could use this array to utilize an indefinite number of parameters:

[js] var myAdder = function(){
var i = 0, result = 0;
//we can check to see if the object in the arguments
//’array’ exists. we also check the type of the input
while(arguments[i] && typeof arguments[i] === ‘number’ ) {
//add it and continue
result += arguments[i];
i++;
}
return result;
}

myAdder(1,2,3,4,5); //15
[/js] JS Bin Demo

Every function also gets a parameter with the keyword ‘this’

We are not going into the details of the this operator in this post. It is sufficient to know that the this operator exists and its meaning depends on how the function is invoked.

Parameters can be any type when passed in to a function

The caller of a function could pass in any type for your parameters. For example, if you have a function which expects a single parameter the caller may choose to pass in:
* nothing (would be undefined in your function)
* a function object
* a string
* a number
* any other object
It is the responsibility of the function to detect and react to these different types. That may seem a bit too loose at first. Yet it opens up incredible expressiveness that allows jQuery to do much of the magic it performs today.

Consider when we use the jQuery (or the alias $) function with one parameter. This core piece of jQuery acts radically different depending on what we specify as the first argument when invoking the function. Here are three examples of passing in different types of objects to the jQuery function and how the function behaves:

[js] $(‘.myClass li’)
// here the jQuery function will determine
// that the first argument is a string. It will
// use the selector engine (sizzle) to determine
// which DOM elements fit the selector, and
// will return a jQuery object with a collection
// of those dom elements
[/js] [js] $(document);
// jQuery recognizes that we have passed
// in a DOM Element. The library returns
// a jQuery object with the collection of 1
// element – the DOM element.
[/js] [js] $(function(){
//…
});
// jQuery recognizes that we have passed
// in a function. It binds the function to
// be fired when the DOM is ready
// this code results in the same behavior as
// using $(document).ready(function(){…
[/js]

This isn’t function overloading. JavaScript has no function overloading in the traditional sense. Yet jQuery radically changes behavior based on the type of our input parameters.

Parameter Strategy: Object Literals

A typical starting point when creating named functions in JavaScript is trying to use a list of parameters. This approach feels familiar to creating functions in statically typed languages. Yet challenges are immediately hit when using a series of named parameters. If the bulk of your arguments are optional you can run into nearly insurmountable issues:

[js] var myUtilityFunction = function(arg1, arg2, arg3) {
//how do we make argument1 or argument2 optional?
};
[/js]

This pattern can work for methods were you have a few parameters and all of them are required. Anytime you extend the function though it becomes more difficult to manage optional parameters. If all of the arguments are the same type it may be impossible to have certain arguments be optional.

A much better pattern would be to pass in a single object literal. In that case we can always look for the specific parameters by name within the object passed in and take action if the object property does not exist:

[js] var myUtilityFunction = function(args){
// this statement is called a ternary statement
// the first portion is a condition. if the
// condition evaluates to true, the next portion (after
// the question mark) will run. Otherwise the
// second statement (after the colon) will run
var param1 = args && args.param1 !== undefined
? args.param1
: "my default 1";
var param2 = args && args.param2 !== undefined
? args.param2
: "my default 2";
// …
};
//…
//usage
myUtilityFunction({
param1 : "my first param",
param2 : "my second param"
});
[/js]

While this pattern is functional we have repeating checking of each parameter. As this function grows our parameter checking would become even uglier. A better solution would be to have a default object literal to merge with the object literal passed in by the caller.

Using Extend to Merge Object Literals

As usual, jQuery provides a great feature to allow us to take object literals and combine them with our defined inline defaults. jQuery.extend takes in any number of objects and combines them into a single object. In our case we will use jQuery.extend to merge a default object literal with the parameter object:

[js] var myFunction = function(args) {
var parameters = $.extend(true, {default1: ‘hello’,
default2: ‘world’},args);
};
[/js]

 

JS Bin demo with Usage

In this example the first parameter lets extend know to do a deep copy of all of the objects.

This provides us with a great solution. If your function can run entirely on defaults then the caller doesn’t have to pass in anything. Additionally we can scale our function by adding extra parameters with defaults which will not break any current code or add additional checks!

Allowing Arbitrary Number of Object Literals

If you have made it this far then congratulations! The above pattern provides a wonderful practice that can be used in many cases when designing functions. We are going to try and take the pattern one extra step. The JavaScript involved in this solution is more advanced. It may be confusing at first. I highly encourage you to read it, play with the examples, and ask any questions you have in the comments.

<

pre>
There are reasons that the caller of our function may have multiple object literals they would like to pass in to our function. For example the calling function may have defaults and passed in parameters. With our current solution, the caller would need to combine those multiple objects using jQuery.extend, and then call our function, which again uses jQuery.extend.

Recall that JavaScript methods can use any number of parameters. If we use the our arguments variable that we get for free with each function, we should be able to combine many object literals together. But how can we use jQuery.extend and the method our own arguments object? An attempt to use jQuery.extend and just passing in our arguments object will not give us our intended behavior:

[js] var myFunction function(){
var parameters = $.extend.apply(true, arguments);
// parameters == ?
// $.extend with only a single object as in this
// example has special meaning. I encourage
// you to investigate. But it is not what we want
// in our case.
};
[/js]

 

JS Bin Demo

JavaScript provides us with a solution. apply is a method that is available on any function. The first parameter of apply is what the keyword this should reference when the function is invoked. The second parameter is an array (or array-like) of the parameters the function will receive in arguments. apply is letting us set up the conditions around which a function is invoked.

In our case we can use apply to set the arguments object of jQuery.extend to be the arguments passed in by our caller:

[js] var myFunc = function() {
// the apply invocation pattern allows us to
// pass arguments directly on to the jQuery.extend
// method. The first parameter is what the ‘this’
// keyword will be set to in the function
// the second parameter will set the arguments
// for the function
var options = $.extend.apply( null, arguments );
// ..
}
}
[/js] JS Bin Demo with Usage

We are close but we are missing our deep copy for jQuery.extend and our defaults. Since arguments is not a true array we can’t do simple concatenation and add true and our default object as the first two parameters. However we can turn the arguments into an array and then concatenate with our extra parameters:

[js] var myFunc = function() {
// we can use Array’s native slice method to
// ‘convert’ the arguments in to a true array
var args = Array.prototype.slice.call(arguments, 0),
defaults = {
// …
},
// the apply invocation pattern allows us to
// pass arguments directly on to the jQuery.extend
// method. The first parameter is what the ‘this’
// keyword will be set to in the function
// the second parameter will set the arguments
// for the function
options = $.extend.apply( null, [true, defaults].concat(args) );
// …
}
[/js]

JS Bin Demo with Usage

Array.prototype.slice allows us to return a true array from our arguments object. We can then create a new array which only includes true and our defaults and then concatenate our arguments on to that new array. We then pass that array in as the arguments for jQuery.extend.

The idea for this solution came from @scott_gonzalez and the jQuery UI team. You can see their implementation of this solution on this jQuery UI GitHub page. The difference in their solution is that they are accounting for the first parameter possibly being a string and not part of the object literals. jQuery UI’s use of this pattern also means that you can pass in any number of object literals to a jQueryUI method (like draggable() or datePicker() ) and the merging will be completed for you.

We now have a great pattern that is extremely flexible. Any number of object literals can come from the caller and we can easily merge them in one place. We can also merge in our default parameters. New parameters with defaults can be added without breaking any caller code.

It feels good to use the powerful parts of a language.