Function Declaration vs Function Expression vs Arrow Function — Right Tool for the Right Job
Contents
- Why I’m writing this?
- An introduction to types of functions in JavaScript
- Explanation on the behaviors of function declarations.
- More precise explanation of function expressions.
- Explanation on the exact usage of arrow functions. Where you should use them and where you should not
Why am I writing this?
The first programming language I learned was C# then I moved to Python because of its popularity.
After sometime, one of my friends suggested JavaScript. Then, I started learning JavaScript. But, I found it difficult to understand, coming from other languages. I got entirely different results from my code. The worst thing is, I couldn’t understand tutorials because of this. At some point, I was frustrated by this language. I’m happy that I didn’t give up.
I dug deep into JavaScript and studied how exactly JavaScript works under the hood. Eventually, I started to love JavaScript. So I am writing this to provide my contribution to the growth of the JavaScript community.
And I notice something strange in the tutorials, blog posts, and articles that people use tools that aren’t meant to be used in such places especially arrow functions.
So, I want to express my views on where the tools best. By reading this article, I hope you will become a better programmer whose code is more readable.
Functions in JavaScript
There are many types of functions in JavaScript. Today we are going to explore:
- Function Declarations
function foo(){}
- Function Expressions
var foo=function foo(){}
- Anonymous Functions
var foo=function(){}
- Arrow Functions
var foo=()=>{}
Each of these functions behaves differently. You can’t replace one with another.
I don’t use anonymous functions and, I strongly recommend you not to use them as well. You’ll see why.
Function declarations
The Syntax
function foo(){// your code}
Behaviors
1. function declaration attach themselves to enclosing scope.
function foo(){//your code}
If you declare a function in the global scope using function declaration, The scope of the function is global. this is one of the key differences compared to function expressions
2. Function declarations are affected by hoisting.
sayHello() // Hellofunction sayHello(){ console.log(“Hello”);}
You can call a function before you declare it. because of hoisting.
Advantages
You can have your executable code on top of all function declarations.
Thanks for hosting! You don’t need to declare all the functions before the executable code. So, you don’t have to scroll to the bottom of your code to see the executable code. instead, keep your executable code at the top and declare all the functions below that.
Best Practice
Use function declaration Generally unless some special occasions.
Function Expressions
The Syntax
var foo=function anotherFoo(){// your code}
Behaviors
1. Function Expressions attach themselves to their own scope.
var foo=function bar(){console.log(bar) // [Function]}console.log(bar)// RefferanceError
Here, bar
is inside its own scope that is why we get an error when we try to access them from outside the scope.
2. Functions Expression are read-only.
var foo=function bar(){ bar= “fooooo”; console.log(bar) // [Function : bar]}
foo();
Here, in the above code, we are assigning bar
to a string and when we run the program we still get [Function:bar]
.
Advantages
Function Expressions are the best way to self-reference.
Disadvantages
The syntax is very complex.
//function declarations
function sayHello(){ // your code }
// function expressions
var sayHello=function sayHello(){ //your code
}
Best Practice
Use function expressions only, when you need self-reference.
Anonymous Functions
The Syntax
var foo=function(){//you code}
Most commonly used as callback.
foo(function(){//your code})
Behaviors
It functions as all the other functions. but, it has no name.
Advantages
The code looks more simpler and cleaner.
foo(function calculateAverage(input){//your code})
//Named functionfoo(function(input){//your code})
//Anonymous functions
You would all agree that coming up with names for functions and variables is so Hard. Anonymous functions solve this problem.
Disadvantages
Your code will not be self-documented — Every time a programmer reads your code he needs to go through all the lines of codes inside a function to understand what this function is doing.
foo(function calculateAverage(input){//your code})
//Named functionfoo(function(input){//your code})
//Anonymous functions
in the above example in Named function we know, the function is to calculate average just by looking at its name. in contrast in the Anonymous function we have to go through the whole body of the code to understand what it’s doing.
You can’t self-reference your function when using function expressions.
var calculate_average=function calculateAverage(input){
console.log(calculteAverage)}
//Named functionvar calculate_average=function(input){console.log(//???)})
//Anonymous functions
As we saw in the function expressions section, we have to function expressions only if we were to self-reference. by using anonymous functions in function expressions you can’t do that.
Best Practice
Don’t ever use anonymous functions.
Arrow Functions
The Syntax
()=>{}
Behavior
To understand the following code you should have a good understanding of this
keyword. if you don’t read my article
var user = {
name:"abc",
foo:foo
}
function foo(){
function bar(){
console.log(this)// Window Object
}
bar()
console.log(this) //{name:"abc",foo:foo}
}
user.foo()
The console.log(this)
statement inside bar
function will log Window
Object
on the console
.
To prevent this, earlier we did the following hack
var user = {
name:"abc",
foo:foo
}
function foo(){
let that = this;
function bar(){
console.log(that)// {name:"abc",foo:foo}
}
bar()
console.log(this) //{name:"abc",foo:foo}
}
user.foo()
The above solution is perfectly fine. But in ES6 JavaScript introduced arrow functions to solve this problem.
var user = {
name:"abc",
foo:foo
}
function foo(){
let bar=()=>{
console.log(this)// {name:"abc",foo:foo}
}
bar()
console.log(this) //{name:"abc",foo:foo}
}
user.foo()
Here, arrow functions solve that problem. because this inside arrow function is undefined. So arrow functions lexically resolve the value of this.
This is the main purpose of arrow functions. So use arrow functions only where you need lexically resolve for this.
eg — In React, when you use class components to use setState()
use Arrow functions.
Advantages
- Lexically resolve for
this
. - Code is very simpler and cleaner.
Disadvantages
Arrow functions are anonymous functions. so all the disadvantages we had for anonymous functions are applicable here.
Best Practice
Don’t ever use arrow functions unless you need lexical behavior
Conclusion
I don’t know about you but, in my code when I review, I keep all executable code at the very top and all the function declarations below that. So most of the time I use function declarations. when I see a function expression in my code I know for sure there is a self-reference.
Likewise, when I see an arrow function I know I wanted some lexical this
behavior inside the function. and I don’t use anonymous functions except for arrow functions. That way my code is more readable.
The usage of right tool for the right job makes your code more readable.
Thank You for Reading.
More content at plainenglish.io