JavaScript Functions
JavaScript functions are capable of more than just merely enclosing a bunch of codes while waiting for the call to execute. Functions have evolved over time leading to new definitions, execution methods and syntaxes. This post will cover some of the present roles JavaScript functions have played so far.
Knowing the different ways of expressing and defining functions opens up the possibility of implementing a logic in a more optimal way in JavaScript. Also, you may be able to answer the interview questions more easily.
Function Expressions
When you simply state a function with function
keyword , optional parameters and body of code, it’s a function declaration.
Put that declaration in a JavaScript expression (like in an assignment or arithmetic expression), it becomes a function expression.
// Function declaration function function_name() {}; // Function expression var function_name = function() {};
All JavaScript declarations are hoisted (moved up in the scope) during evaluation. Hence writing a function call before the function declaration is okay (since the declaration will be moved up anyway).
function_name();//function call[WORKS] function function_name(){};
Function expressions however aren’t hoisted since the functions become part of the expressions and are not stand-alone declarations.
function_name();//function call[WON'T WORK] var function_name = function(){};
Immediately Invoked Function Expression (IIFE)
It’s a function expression, the code of which gets executed immediately (only once when it’s evaluated). You can create one by simply adding ()
(syntax used for calling a function) right after a function expression. They can be anonymous (no name to call it with).
Below are the two most common syntaxes to create IIFE:
(function optional_function_name() { //body }());
and
(function optional_function_name() { //body })();
The parenthesis around the function declaration converts it to an expression and then adding ()
after it calls the function. You can use other ways to create IIFE for as long as you add ()
after a function expression (like below), but the preferred methods are the above two.
// Some of the ways to create IIFEs !function() { /* ... */ }(); +function() { /* ... */ }(); new function() { /* ... */ };
IIFE is ideal for writing code that needs to execute only once, namespacing, creating closures, creating private variables and more. Below is an example of IIFE use.
var page_language = (function () { var lang; // Code to get the language of the page return lang; })();
The code to get the page’s language executes only once (preferably after the page loads). The result is stored in page_language
for later use.
Methods
When a function is an object’s property, it is called method. Since a function is also an object, a function inside another function is also a method. Below is an example for a method inside object.
var calc = { add : function(a,b){return a+b}, sub : function(a,b){return a-b} } console.log(calc.add(1,2)); //3 console.log(calc.sub(80,2)); //78
The add
and sub
functions are methods of calc
object.
Now for a function within function example:
function add(a){ return function(b){return a+b;} } console.log(add(1)(2)); // Output is 3
The returned anonymous function is a method of function add
.
Note: Since parameter (a
) of function add
in the above example is available for the following function invoke, this type of process is called currying.
Constructors
When you add new
keyword before a function and call it, it becomes a constructor that creates instances. Below is an example where constructors are used to create instances of Fruit
and values are added to each Fruit
‘s properties.
function Fruit(){ var name, family; // Scientific name & family this.getName = function(){return name;}; this.setName = function(value){name=value}; this.getFamily = function(){return family;}; this.setFamily = function(value){family=value}; } var apple = new Fruit(); apple.setName("Malus domestica"); apple.setFamily("Rosaceae"); var orange = new Fruit(); orange.setName ("Citrus × sinensis"); orange.setFamily ("Rutaceae"); console.log(orange.getName()); // "Citrus × sinensis" console.log(apple.getName()); // "Malus domestica" console.log(orange.getFamily()); // "Rutaceae"
Arrow Functions (ES6 Standard) [Only in Firefox]
A new function definition from ES6 Standard provides a shorter syntax for function expression. The syntax is
() => { /* body */ }
This sample function:
var sing = function(){ console.log('singing...') };
is the same as:
var sing = () => { console.log('singing...') };
Arrow functions are anonymous and does not have its own this
value, this
inside it will be same as this
in the enclosing code. Also, you cannot change it to a constructor with new
keyword.
They are useful for when you want this
inside a function to be the same as outside and its shorter syntax makes code for writing function within function concise (like below)
setInterval(function () { console.log('message') }, 1000);
into
setInterval(() => console.log('message'), 1000);
Generator Functions (ES6 Standard) [Only in Firefox]
Another new function definition from ES6 Standard is Generator Function. Generator functions are capable of halting and continuing its execution. Its syntax is:
function* function_name(){}
or
function *function_name(){}
Generator functions create iterators. The iterator’s next
method is then used to execute the code inside the generator function until the yield
keyword is reached. After that, the iterated value identified by the yield
keyword is returned by the generator function and the execution is halted.
The generator function again executes when the next
method is called until the next yield
keyword is reached. Once all of the yield
expressions are executed, the yielded value returns undefined
.
Below is a simple example:
function *generator_func(count) { for(var i=0;i<count;i++){ yield i+1; } } var itr = generator_func(4); console.log(itr.next()); //Object { value: 1, done: false } console.log(itr.next()); //Object { value: 2, done: false } console.log(itr.next()); //Object { value: 3, done: false } console.log(itr.next()); //Object { value: 4, done: false } console.log(itr.next()); //Object { value: undefined, done: true } console.log(itr.next()); //Object { value: undefined, done: true }
Here’s another example:
function *randomIncrement(i) { yield i + 3; yield i + 5; yield i + 10; yield i + 6; } var itr = randomIncrement(4); console.log(itr.next().value); //7 console.log(itr.next().value); //9 console.log(itr.next().value); //14
There’s also a yield*
expression which passes the value to another generator function
function *fruits(fruit) { yield* veggies(fruit); yield "Grapes"; } function *veggies(fruit){ yield fruit + " and Spinach"; yield fruit + " and Broccoli"; yield fruit + " and Cucumber"; } var itr = fruits("Apple"); console.log(itr.next().value); //"Apple and Spinach" console.log(itr.next().value); //"Apple and Broccoli" console.log(itr.next().value); //"Apple and Cucumber" console.log(itr.next().value); //"Grapes" console.log(itr.next().value); //undefined
Generator functions are useful if you want to go through values one by one at your preferred point in the code by pausing it, rather than in one go like in looping through an array.
Conclusion
I’ve included a list of references below, where you will find links to references and articles that go in-depth on different topics separately. Both the ES6 standard functions will work only in Firefox at the moment.