JavaScript Function Notations
Linas Spukas
Posted on January 26, 2020
JavaScript lets you create functions in a few different ways. And depending on how you do it, the execution and result of the function will slightly diverse.
In this article, I will briefly go through three different notations: declaration, expression and arrow functions, compare the differences, and when it is more useful to use one over another.
Function Declaration
When function created with the global keyword function
, followed by the name and optional argument list, is called a function declaration and has all the methods (bind, apply, call..) and properties (name, length, arguments) of a Function object:
function greet() {
console.log('hello');
}
greet.name // greet
greet.length // 0
A function declaration has an advantage comparing other notations - its hoisting. Meaning, it does not follow the regular code flow from top to bottom. When JavaScript executes the code, first it looks for the global keyword function
and hoists the function at the top of its scope. This means the function can be declared at the bottom of the code block and invoked at the beginning, it will still be executed correctly:
// function invoked before it was declared
greet() // hello
function greet() {
console.log('hello');
}
Hoisting gives you more flexibility to put the functions in a more meaningful place in your code without worrying about the order. And personally, function declarations are easier to read.
Function Expression
Function expression also creates a Function object with all its properties. The small difference is that you do not need to specify the name because function value is bound to a variable. Also, it should end with a semicolon. If the expression was created as an anonymous function, its name will represent the variable name that is bound to it:
const greet = function() {
console.log('hi')
};
greet() // hi
greet.name // greet
const greet = function logGreeting() {
console.log('hi')
};
greet.name // logGreeting
The main difference from the function declaration is hoisting. Expressions are not hoisted, thus the order where are they created greatly matters. It should follow the code flow from top to bottom:
greet() // will throw ReferenceError
const greet = function() {
console.log('hi')
};
greet() // hi
You can benefit from function expressions when creating a conditional function or immediately invoked ones:
// conditional function
let greet;
if (true) {
greet = function() {
console.log('hi');
};
} else {
greet = function() {
console.log('bye');
}
}
greet(); // hi
// immediately invoked
const result = (function() {
return 2;
})();
result; // 2
Furthermore, if you want to refer function expression inside itself, you have to give it a name. The case would be recursion:
const getSum = function sum(num, total = 0) {
if (num === 0) {
return total;
}
return sum(num - 1, total + num);
};
getSum(3) // 6
Arrow Function
They were introduced in ES6 to make it possible to write smaller and simpler functions. The syntax of an arrow function starts with parentheses or arguments and =>
sign which follows the expression:
const addFive = (num) => {
return num + 5;
}
addFive(3) // 8
If arrow function expresses a single line of code, it can be shorted by removing curly braces:
const addFive = (num) => num + 5;
addFive(3) // 8
Though arrow functions look very similar to function expressions, they do not have a complete Function object inheritance. They miss their own bindings to this
, arguments
and super
keywords.
As arrow functions do not have their own this
keyword, it doesn't mean a bad thing. It can see this
binding of the scope around them. It is useful when writing class methods or passing callback functions to array methods because you do not need to bind the context manually, it is done for you by the arrow function. For example:
// with function declaration inside the 'map' method
function increment() {
console.log(this.numbers.map(function(val) { return val + this.add }));
}
// will not recognise 'this.add', because `function` has its own `this`
// which is does not reference to `increment` context
increment.call({ numbers: [1, 2, 3], add: 5 }); // [undefined, undefined, undefined]
// with arrow function inside the 'map' function
function increment() {
console.log(this.numbers.map((val) => val + this.add));
}
// will return correct mapped result
increment.call({ numbers: [1, 2, 3], add: 5 }); // [6, 7, 8]
Conclusion
There is no better or worse way to notate functions in your code. Each way of a function creation has it's own benefits and should be taken into consideration where will it be used. Though most of the time it should be enough to write function declarations.
Posted on January 26, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.