Quick Tip: Function Expressions vs Function Declarations
JavaScript has two different ways of creating functions. Function declarations have been used for a long time, but function expressions have been gradually taking over.
function funcDeclaration() {
return 'A function declaration';
}
var funcExpression = function () {
return 'A function expression';
}
Differences between Declarations and Expressions
Similar to the var
statement, function declarations are hoisted to the top of other code. Function expressions aren’t hoisted, which allows them to retain a copy of the local variables from the scope where they were defined.
Normally function declarations and function expressions can be used interchangably, but there are times when function expressions result in easiler to understand code without the need for a temporary function name.
Benefits of Function Expressions
There are several different ways that function expressions become more useful than function declarations.
- As closures
- As arguments to other functions
- As Immediately Invoked Function Expressions (IIFE)
Creating Closures
Closures are used when you want to give parameters to a function, before that function is executed. A good example of how this can benefit you is when looping though a NodeList
. A closure allows you to retain other information such as the index, in situations where that information will no longer be available when the function is executed.
function tabsHandler(index) {
return function tabClickEvent(evt) {
// Do stuff with tab.
// The index variable can be accessed from within here.
};
}
var tabs = document.querySelectorAll('.tab'),
i;
for (i = 0; i < tabs.length; i += 1) {
tabs[index].onclick = tabsHandler(i);
}
The attached event handlers are executed at a later time (after the loop is finished), so a closure is needed to retain the appropriate value of the for loop.
// Bad code, demonstrating why a closure is needed
var i;
for (i = 0; i < list.length; i += 1) {
document.querySelector('#item' + i).onclick = function doSomething(evt) {
// Do something with item i
// But, by the time this function executes, the value of i is always list.length
}
}
It’s easier to understand why the problem occurs by extracting the doSomething()
function out from within the for loop.
// Bad code, demonstrating why a closure is needed
var list = document.querySelectorAll('.item'),
i,
doSomething = function (evt) {
// Do something with item i.
// But, by the time this function executes, the value of i is not what it was in the loop.
};
for (i = 0; i < list.length; i += 1) {
item[i].onclick = doSomething;
}
The solution here is to pass the index as a function argument to an outer function, so that it can pass that value to an inner function. You’ll commonly see handler functions used to organize the information that an inner returning function needs.
// the following is good code, demonstrating the use of closure
var list = ['item1', 'item2', 'item3'],
i,
doSomethingHandler = function (itemIndex) {
return function doSomething(evt) {
// now this doSomething function can retain knowledge of
// the index variable via the itemIndex parameter,
// along with other variables that may be available too.
console.log('Doing something with ' + list[itemIndex]);
};
};
for (i = 0; i < list.length; i += 1) {
list[i].onclick = doSomethingHandler(i);
}
Other good examples of closures and how they can help can be found at this closure examples FAQ.
Passing as Arguments
Function expressions can be passed directly to functions without having to be assigned to an intermediate temporary variable.
They can be frequently seen in jQuery in the form of an anonymous function, for example:
$(document).ready(function () {
console.log('An anonymous function');
});
Also when using methods such as forEach()
, a function expression is used to handle the array items. They don’t have to be unnamed anonymous functions either. It’s a good idea to name the function expression to help express what the function is supposed to do, and to aid in debugging.
var productIds = ['12356', '13771', '15492'];
productIds.forEach(function showProduct(productId) {
...
});
Immediately Invoked Function Expressions (IIFE)
IIFE’s are used to help prevent your functions and variables from affecting the global scope. All the properties within are scoped to the anonymous function. This is a common design pattern that’s used to prevent your code from having unwanted or undesired side-effects elsewhere.
It’s also used as a module pattern to contain blocks of code in to easy to maintain sections. We take a deeper look at at these in Demystifying JavaScript closures, callbacks and IIFEs.
A simple example of an IIFE is:
(function () {
// code in here
}());
which when used as a module, can result in some easy-to-achieve maintainability for your code.
var myModule = (function () {
var privateMethod = function () {
console.log('A private method');
},
someMethod = function () {
console.log('A public method');
},
anotherMethod = function () {
console.log('Another public method');
};
return {
someMethod: someMethod,
anotherMethod: anotherMethod
};
}());