Table of contents
- Why Do we need functions?
- How to declare a function?
- Difference between Function Declaration &
- Definition?
- What is the difference between Parameters and arguments?
- How to define Function Expressions?
- How to return from a function
- What are default parameters?
- What are Rest Parameters?
- How to define Arrow Functions?
- What is the Execution context in JavaScript?
- How scopes are different in variables?
- What is a window object?
- What are Nested Functions?
- What is the scope of a Function?
- What is Lexical Scope?
- How does the Scope Chain work?
- What are Closures?
- What is a callback function?
- setTimeout
- What are higher-order Functions?
Why Do we need functions?
Functions serve as a way to encapsulate a set of instructions into a single unit that can be invoked and reused throughout your code. Imagine you have a block of code that you use repeatedly. (Alright... alright... Don't leave, this article is worth a read. Just stick around for a little while.)
Instead of copying and pasting it every time, you can place it within a function and simply call that function whenever you need to execute that code. This not only promotes code reusability but also enhances the readability and maintainability of your codebase, making it easier to manage, debug, and update as your project grows.
How to declare a function?
FunctionName is like a nickname you give someone – it helps you recognize and call them. Just like you might say "Adarsh" or "heyBaby!"
In some situations, functions can work without a name at all, so keep that in mind.
Difference between Function Declaration &
Definition?
You've probably heard programmers say, "I've declared a function" or "I've defined the function." These phrases are pretty much the same thing.
Now, let's take the next step and declare a function ourselves.
function printMe(){
console.log('printing...')
}
To make this function work, you need to 'call' or 'invoke' it. Think of how you call someone in real life – you use their name. Right? Similarly, to use our function, you simply call it by its name.
But here's the catch: if you just write the function's name in JavaScript, it won't give you the result you expect. Instead, you'll see the entire function body as a "string" version of the function definition.
To get the desired result, you must use parentheses ()
after the function name.
What is the difference between Parameters and arguments?
Now, let's dig into a key concept: parameters and arguments. Think of parameters as variables waiting to be filled in a function, and when we use the function, we provide actual values, which are the arguments.
It's a bit like when you ask a friend for money, you ask a particular amount (parameter), and they start abusing you (argument). So, let's put this into practice by adding parameters to our function and passing in argument values.
How to define Function Expressions?
Now that we've got a grip on a function declaration and passing values, let's explore another method of defining functions using a function expression.
Think of function expression as assigning value to a variable.
For those who might not be familiar, JavaScript has three types of variables:
const: This is used for values that won't change.
let and var: These are used for values that can change. We'll dive deeper into these when we explore function scope, but for now, let's keep it at that.
So, in a function expression, we're kind of swapping out variables with functions. variable name turning into the function's name, and instead of a regular value, we're putting an entire function in its place
const printMe = function(){
console.log("this is function expression");
}
printme()
//output:
//this is function expression
Now, let's take our understanding of function expressions a step further by using parameters and arguments.
const printMe = function(a,b,c,d,e,f) {
console.log(a,b,c,d,e,f);
}
printMe(10,20,30,40,50,60)
If you're paying close attention, you might have realized that we can treat functions as values in JavaScript. In programming languages that treat functions as values, these functions are referred to as first-class citizens.
How to return from a function
Imagine we have two functions: Function X and Function Y. Each of them is skilled at doing their own thing.
Now, let's say Function X completes its task and produces a result. Instead of letting that result vanish into thin air, we can save it for later. This saved result can be used when we call Function Y.
function x(){
/*-----------------
---Their own task--
-------------------*/
}
const p = x(); //saving for future use
function y(){
/*-----------------
---Their own task--
-------------------*/
}
Now, let's dive into the code to gain a deeper understanding. Go through each step with specific numbers, making it easier to grasp the concept more effectively.
What are default parameters?
What if the function doesn't return any value?
For example, if you forget to provide the necessary arguments that a function expects, the function will still execute, but the function's execution will result in a special value called "undefined" or "nan" (not a number).
That brings us to our new topic Default Parameters.
We can use the default parameters value for a function if it's required to safeguard it from an unnatural value returning from the function. Let's understand better with the code.
Now what will happen if we only pass one argument?
Now in this case you are not passing the second argument, So b will be undefined
So we can safeguard our parameters with some kind of default values, something that you like.
What are Rest Parameters?
The rest parameter is something that allows a function to accept any number & infinite number of arguments as an array
.
Just remember, you can use only one rest parameter and it must be the last one in the list of parameters.
How to define Arrow Functions?
Arrow functions
or fat-arrow syntax
we know how to define a function with the function expression.
const add = function (x,y){
return x+y;
}
Let's take a look at converting a regular function expression to an arrow function, with a few adjustments that you have to make.
If the arrow function body contains just one statement, you can skip adding curly braces.
const add = (x,y) => x+y;
And if you're dealing with just one parameter, you can leave out the parentheses as well.
const add = x => x;
What is the Execution context in JavaScript?
Whenever a function is executed it creates its imaginary container. Imagine each function as having its little room. This room holds everything the function needs while it's working. It's like a workspace where the function can access its tools (variables and functions) and look out the window to see what's happening around it (scope chain). This setup ensures that each function can do its job without getting mixed up with other functions.
How scopes are different in variables?
In JavaScript, we commonly use three types of variables, Let's delve into these differences with some code examples
var
, let
, and const
. var
was introduced in ES5, and let
and const
were introduced in ES6.
Variables declared with var
have a function scope, making them accessible throughout the entire function they're declared in.
variables declared with const
have block scope, limiting their visibility to the specific block or curly braces {}
in which they are defined.
var
attaches itself to the global window
object, which can lead to data leakage and global variable interference across different parts of your code or even other pages.
let
and const
, are more localized and prevent unintended global scope issues.
What is a window object?
JavaScript can use certain features that aren't even included in the programming language itself. These functionalities are made available by browsers like Chrome, Safari, and others through objects like the window object.
console
, prompt
, onclick
etc. are all components of the window object. While they're not built directly into JavaScript itself, they are provided by browsers.
We can use them in our JavaScript code to enhance our web applications, even though they're not a fundamental part of the core JavaScript language.
It's worth noting that there are additional objects beyond the window object
as well.
What are Nested Functions?
What does nested mean? We know how to define a function
In JavaScript, you can make a function inside another function. This is like the first puzzle piece for understanding something cool called 'closure.' And if you're curious about closures, start by getting comfortable with nested functions – that's one function inside another.
Let's find out why we're seeing a 'ReferenceError,' which brings us to our next topic: function scope.
What is the scope of a Function?
Let's make it clear. You've got to know two important rules, especially if you're digging into closures. Ready?
- Variables Scoped Within Functions: Variables that are defined inside a function can not be accessed outside of the function.
- The opposite of it: A function can access anything and everything inside the scope it is defined.
Function scope is about what variables a particular function can access. If you have a variable defined inside a function, it's like a secret that only that function knows. Other functions can't peek into that secret unless it's shared.
What is Lexical Scope?
Let's use code to make this clearer. It's all revision boys!
All the details about which function can access which variables and how the scope hierarchy is structured are stored in something called the lexical environment.
It's like a map that keeps track of who can access what, ensuring that each function knows its scope boundaries and which variables it can reach. This lexical environment is a behind-the-scenes assistant that helps JavaScript keep everything organized and prevents variables from wandering where they shouldn't be.
How does the Scope Chain work?
If one scope needs to use a certain variable but cannot find it in the scope, it will look up in the scope chain and check whether it can find a variable on one of the outer scopes. If the variable is available in the outer scope, then the child scope has access to it. If it is not there in any outer scopes, the JavaScript will throw a reference error. So this process is called variable lookup.
Let's understand better with some code.
We're invoking the getPersonInfo
function, which returns a string containing the values of the name
, age
and city
variables:
First, memory space is set up for the different contexts. Understand context as some rooms. The scope chain gets created when the execution context (like here getPersonInfo()
is called ) is created, meaning it's created at runtime! and each room is like a mini-environment that holds variables and functions.
Mainly there are two types of contexts in the scope chain: the global context and local contexts (execution contexts) created when functions are called.
We have the default global context (window
in a browser, global
in Node), and a local context for the getPersonInfo
function which has been invoked. Each context also has a scope chain.
For the getPersonInfo
function, the scope chain looks something like this (don't worry, it doesn't have to make sense just yet):
The scope chain is like a "sequence of references" (connections) to objects. These objects contain information about references to values and other scopes that can be accessed within a specific part of your code.
What will happen when it tries to access the city
?
JS engine doesn't give up that easily: it works hard for you to see if it can find a value for the variable city
in the outer scope that the local scope has a reference to until it hits the global object.
Now that we have a value for the variable, the function getPersonInfo
can return the string Sarah is 22 and lives in San Francisco
Let's take another code as an example.
It's almost the same, however, there's one big difference: we only declared a city
in the getPersonInfo
function now, and not in the global scope. We didn't invoke the getPersonInfo
function, so no local context is created either. Yet, we try to access the values of name
, age
and city
in the global context.
(Rule 2: You can go to outer scopes, but not to more inner... scopes.)
Quick Recap:
JavaScript starts looking for a variable from where you're standing.
If it doesn't find it, it goes up one level in the scope chain and checks again.
This repeats until the variable is found or it reaches the global scope.
What are Closures?
Closure is nothing but a nested function.
Wait! Wait! Why all the hype about the closure then?
Closure understanding depends on your understanding of nested functions & function scopes.
Closures are functions that have access to variables from another function's scope. This is accomplished by creating a function inside another function.
A Closure is a function that returns another function.
A Closure is an implicit, permanent link between a function and its scope chain.
So, closure is nothing but a nested function.
What we basically do in closure is call the outer
function, specify an argument
, and then leverage both the inner and outer functions together.
Now what will happen if I call outer
function in the same code with an argument?
So what does outerReturn
now contains?
Though the execution of x
is over, the value that we have passed through the outer
function still lives inside the inner
function.
A closure is a way in JavaScript that a function can remember and access its outer variables, even after the function has finished executing. It's like a little memory bubble that lets functions hold onto things they need, even if they're done doing their main job. So, closures are like JavaScript's superpower for managing data and making sure functions play nice with each other.
What is a callback function?
We have created functions:
function definition
We have assigned functions:
function expression
We have defined functions in another function:
Nested functions
Now we are going to pass the function as a parameter
to another function.
if bar
had been a string I would have printed it, right? If it was a number I could have done maths around it. So if it is a function then I can execute it. Right?
How do I execute this?
Now I know that foo
takes a function, so I can pass a function to it.
A callback function
is a function that is passed as an argument
to another function and is intended to be executed later.
We can do this more directly by using anonymous functions
.
Notice, that we have created a function without any name, this function is called an anonymous function
, If you remember our chat on "how we declare functions?", this might ring a bell.
setTimeout
It is a built-in JavaScript function that allows you to schedule the execution of a function after a specified amount of time. It's basically what we learned with a time delay feature.
So, now this code will run after 10 seconds. It will come in handy when we learn Async programming.
Now just to make it all things clear. We will improvise our previous example with setTimeout.
What are higher-order Functions?
A higher-order function is a special type of function that does one of two things: it either takes another function as an input or returns a function as its output.
You might recall this concept from our discussion on callback functions, where we touched on the idea. However, not all callback functions are necessarily higher-order functions.
If you're curious to learn more, you can explore topics like pure and impure functions, as well as Immediately Invoked Function Expressions (IIFE) on your own.
Keep this blog as a reference because I might update more function-related concepts of JS in this blog. Happy learning!
Special thanks: Lydia Hallie