ref – https://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/
What is a Function Declaration?
A Function Declaration defines a named function variable without requiring variable assignment.
Function Declarations occur as standalone constructs and cannot be nested within non-function blocks.
It’s helpful to think of them as siblings of Variable Declarations. Just as Variable Declarations must start with “var”, Function Declarations must begin with “function”.
1 2 3 |
function funcDeclaration() { return 'A function declaration'; } |
a function declaration:
1 2 3 4 5 6 |
function bar() { return 3; } bar() //3 bar // [function] |
ECMA 5 (13.0) defines the syntax as
function Identifier ( FormalParameterListopt ) { FunctionBody }
Similar to the var statement, function declarations are hoisted to the top of other code.
What is a Function Expression?
A Function Expression defines a function as a part of a larger expression syntax (typically a variable assignment ). Functions defined via Functions Expressions can be named or anonymous.
Function Expressions must not start with “function”
Rather, they start with a var reference, or a parentheses.
1 2 3 |
var funcExpression = function () { return 'A function expression'; } |
more examples:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//anonymous function expression var a = function() { return 3; } //named function expression (bar) var a = function bar() { return 3; } //self invoking function expression (function sayHello() { alert("hello!"); })(); |
Function expressions aren’t hoisted, which allows them to retain a copy of the local variables from the scope where they were defined.
Benefits of Function Expressions
There are several different ways that function expressions become more useful than function declarations.
As 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.
Let’s create a unordered list with class “tab”:
1 2 3 |
var tabs = document.getElementsByClassName("tab"); var i = 0; console.dir(tabs); // element list |
Then we loop over the elements, and assign it a click event like so:
1 2 3 4 5 |
for (i = 0; i < tabs.length; i += 1) { console.log("Creating function for tab: " + i) tabs[i].onclick = standardFunction; } |
Note that, tabs[i] is an HTML Element object. It has an “onclick” callback handler.
We need to provide it a function so that the HTML element tabs[i] has a callback function to execute after running its own code.
the HTML element tabs[i] will also pass event object (filled with info on the click itself)
thus, when we click, it’ll call our “standardFunction”
and also we get to analyze (and use) an event object.
In terms of scope, you can access parent’s scope variable i. However, note that the loop runs first, going from 0 to 2, and assigning all callback functions first in the process. After it finishes assigning the function, the i is at 2.
When you click, the callback will execute and since our scope is parent, it will access i, which is 2.
This isn’t what we want. We want to keep track of the index.
Solution
The solution to keeping track of this index is to use a closure. Closures are objects and depending on implementation (gets moved around from stack to heap). Its a way to reference scope and variables. In our case, we first create a function called rickysHandler where we pass in the index i.
rickysHandler’s has a inner function called tabClickEvent, which is returned to a tab’s onclick.
Whenever a click happens, it will execute this tabclickEvent.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function rickysHandler(index) { console.log("tabsHandler <--"); console.log("Index is: "+index); console.log("tabsHandler -->") return function tabClickEvent(evt) { console.log(evt) console.log(index) } } ... tabs[i].onclick = rickysHandler(i); ... |
rickysHandler takes the index and creates a closure for tabClickEvent. Under the hood, whenever tabs[i].onclick gets assigned to a rickysHandler(i), it creates a closure where i lives. And then the function tabClickEvent references it.
so tabs[0].onclick references its own function tabClickEvent, where it reference a closure with index 0
so tabs[1].onclick references its own function tabClickEvent, where it reference a closure with index 1
so tabs[2].onclick references its own function tabClickEvent, where it reference a closure with index 2
like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function rickysHandler(index) { console.log("tabsHandler <--"); console.log("Index is: "+index); console.log("tabsHandler -->") return function tabClickEvent(evt) { console.log(evt) console.log(index) } } var tabs = document.getElementsByClassName("tab"); var i = 0; console.dir(tabs); // element list for (i = 0; i < tabs.length; i += 1) { //tabs[i].attachEvent('onclick', tabsHandler); console.log("Creating function for tab: " + i) tabs[i].onclick = rickysHandler(i); } |
Passing as Arguments
Function expressions can be passed directly to functions without having to be assigned to an intermediate temporary variable.
The Array prototype has a forEach function can use to iterate through all the elements. The forEach function takes a function expression. We create an anonymous function expression and pass it in.
1 2 3 4 5 6 |
var tabsArray = Array.from(tabs) tabsArray.forEach(function(item, index){ console.log(item) console.log(index) }); |
As 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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 }; }()); |