In this blog post, we will dive into the world of JavaScript functions. Whether you’re new to programming or an experienced developer, this guide will provide you with a solid understanding of functions and how to use them effectively.
Table of Contents
- Introduction
- Syntax
- Parameters
- Return Values
- Nested Functions
- Object Methods
this
in Arrow Functions- IIFE, Immediately Invoked Function Expressions
- Function Hoisting
Introduction
Functions are the building blocks of JavaScript programming. They are self-contained blocks of code that can be defined once and executed multiple times. In JavaScript, functions are special objects called “function objects” and they have the ability to be invoked.
Additionally, functions in JavaScript are considered “first class functions”, meaning they can be assigned to variables, passed as arguments, and used as return values.
Syntax
Let’s start with the syntax for defining functions. In pre-ES6/ES2015, functions can be declared using the following syntax:
function doSomething(foo) {
// do something
}
In the post-ES6/ES2015 world, this is referred to as a “regular function”. Functions can also be assigned to variables, which is called a “function expression”:
const doSomething = function(foo) {
// do something
}
Named function expressions are similar, but they have the advantage of holding the name of the function in the stack call trace, making it useful for error handling:
const doSomething = function doSomething(foo) {
// do something
}
ES6/ES2015 introduced a new syntax called “arrow functions”, which are especially useful for inline functions, parameters, and callbacks:
const doSomething = foo => {
// do something
}
It’s important to note that arrow functions have a key difference from the other function definitions we mentioned earlier. We’ll discuss this difference later in the blog post.
Parameters
Functions in JavaScript can have one or more parameters. Here are some examples:
const doSomething = () => {
// do something
}
const doSomethingElse = foo => {
// do something
}
const doSomethingElseAgain = (foo, bar) => {
// do something
}
Starting from ES6/ES2015, functions can also have default values for parameters:
const doSomething = (foo = 1, bar = 'hey') => {
// do something
}
This allows you to call a function without providing values for all the parameters:
doSomething(3);
doSomething();
ES2018 introduced trailing commas for parameters, which helps prevent bugs caused by missing commas when rearranging parameters:
const doSomething = (foo = 1, bar = 'hey') => {
// do something
}
doSomething(2, 'ho!');
You can also use the spread operator to pass an array of arguments to a function:
const doSomething = (foo = 1, bar = 'hey') => {
// do something
}
const args = [2, 'ho!'];
doSomething(...args);
For functions with many parameters, you can use object destructuring to maintain the parameter names:
const doSomething = ({ foo = 1, bar = 'hey' }) => {
// do something
console.log(foo); // 2
console.log(bar); // 'ho!'
}
const args = { foo: 2, bar: 'ho!' };
doSomething(args);
Return Values
Every function in JavaScript returns a value, which by default is undefined
. You can explicitly return a value using the return
keyword:
const doSomething = () => {
return 'test';
}
const result = doSomething(); // result === 'test'
A function can only return one value. However, you can simulate returning multiple values by returning an object literal or an array, and using destructuring when calling the function:
const doSomething = () => {
return ['one', 'two'];
}
const [val1, val2] = doSomething(); // val1 === 'one', val2 === 'two'
const doSomething = () => {
return { prop1: 'one', prop2: 'two' };
}
const { prop1, prop2 } = doSomething(); // prop1 === 'one', prop2 === 'two'
Nested Functions
Functions can be defined inside other functions. These are called nested functions. The nested function is scoped to the outer function and cannot be called from outside. This allows you to create encapsulated code that is limited in scope to the outer function:
const doSomething = () => {
const doSomethingElse = () => {
// some code here
}
doSomethingElse();
return 'test';
}
doSomethingElse(); // ReferenceError: doSomethingElse is not defined
Nested functions are useful for creating modular and encapsulated code within functions. You can have multiple functions with the same name inside different outer functions, without worrying about overwriting existing functions and variables.
Object Methods
When a function is used as an object property, it is called a method. It can be invoked using the dot notation:
const car = {
brand: 'Ford',
model: 'Fiesta',
start: function() {
console.log(`Started`);
}
}
car.start(); // Output: Started
this
in Arrow Functions
There is an important difference in how this
is handled in arrow functions compared to regular functions when used as object methods. Consider the following example:
const car = {
brand: 'Ford',
model: 'Fiesta',
start: function() {
console.log(`Started ${this.brand} ${this.model}`);
},
stop: () => {
console.log(`Stopped ${this.brand} ${this.model}`);
}
}
In this example, the stop()
method does not work as expected. The reason is that arrow functions handle this
differently than regular functions. In arrow functions, this
refers to the enclosing function context, which in this case is the window
object.
For object methods, it is recommended to use regular functions instead of arrow functions. Arrow functions are not suitable for object methods and can cause unexpected behavior.
IIFE, Immediately Invoked Function Expressions
An IIFE (Immediately Invoked Function Expression) is a function that is executed immediately after its declaration. It is commonly used to create a private scope and prevent pollution of the global scope.
;(function doSomething() {
console.log('executed');
})();
It is also possible to assign the result of an IIFE to a variable:
const something = (function doSomething() {
return 'something';
})();
IIFEs are particularly useful when you want a function to execute once without being called separately.
Function Hoisting
JavaScript reorders your code before executing it, and functions are moved to the top of their respective scopes. This is known as hoisting. It allows you to call a function before it’s defined in the code:
doSomething();
function doSomething() {
console.log('did something');
}
Internally, JavaScript moves the function declaration to the top:
function doSomething() {
console.log('did something');
}
doSomething();
However, when using named function expressions, the variable declaration is hoisted but not the value, so the function won’t work as expected:
doSomething();
const doSomething = function doSomething() {
console.log('did something');
}
This is because the actual code execution is as follows:
const doSomething;
doSomething();
doSomething = function doSomething() {
console.log('did something');
}
The same hoisting concept applies to let
declarations. On the other hand, var
declarations are hoisted and initialized with the value undefined
.
By understanding how function hoisting works, you can write organized and maintainable code.
Conclusion
In this blog post, we covered the various aspects of JavaScript functions, including syntax, parameters, return values, nested functions, object methods, this
in arrow functions, IIFE, and function hoisting. Functions are a fundamental part of JavaScript programming, and mastering them will greatly improve your coding skills.
Tags: JavaScript, Functions, ES6, Syntax, Parameters, Return Values, Nested Functions, Object Methods, this
, Arrow Functions, IIFE, Function Hoisting.