🪧JS  Interview  Preparation Cheat sheet 🪧

🪧JS Interview Preparation Cheat sheet 🪧

Hola! Welcome back, In this article, we are going to master JS's important concepts in such a way that you won't be forgetting them again. So let's get started ➡️

1. Scope and Scope Chain:

🔭 1. Scope

Here, let's learn what scope and a scope chain are, why they are so important and how they work. And let's start by understanding what scoping actually means, and learn about some related concepts as well.

👉 Scoping:

How our program's variables are organized and accessed Where do variables live? OR Where can we access a certain variable, & where not?

👉 Lexical Scoping:

Scoping is controlled by the placement of functions and blocks in the code

👉 Scope:

Space or environment in which a certain variable is declared(variable environment in case of functions). There is a global scope and a 'local' scope.

👉 Scope of a variable:

Region of our code where a certain variable can be accessed

🤔 Now let's understand three different types of scope

A) Global Scope

Variables declared outside of functions or code blocks (curly braces { }) are considered to have a global scope. The outermost scope contains the entire code, and there is only one global scope in the program.

The variables defined in the global scope are named Global Variables and can be accessed and altered in any other scopes.

Code example-

global.png

Key points:

🗝️Outside of any function or block.

🗝️Variables declared in the global scope are accessible everywhere


B) Function Scope

Each and every function creates its own scope. And the variables declared inside that function are only accessible inside that function and any of its nested functions. This is also called Local Scope.

Code example-

functscop.png

Key points:

🗝️Variables are accessible only inside the function, Not outside the function.

🗝️Also called Local scope


C) Block Scope

ES6 introduced let and const variables. With that, it introduced the block scope. Block scope means that the variables defined inside a code block {} can only be used inside it.

For example, a variable created inside an if statement or for loop can only be accessed within that code block. Same as function scope, it is not accessible outside of the block scope.

While let and const are block-scoped, the variables defined with var have their scope limited to the current function scope or the global scope. Suppose we declare a variable using var, that variable is accessible outside the block. So, the variable declared using var within a code block is not block scoped; It is function scoped.

Code example-

3.png

Key points:

🗝️ Variables are accessible only inside the block (block scoped)

🗝️However, this only applies to let and const variables!

🗝️ Functions are also block scoped (only inside the strict mode).


Now before moving to Scope-Chain let's understand lexical scoping in detail

👀 Lexical Scoping in detail:

Lexical scoping means that organizing and accessing variables are controlled by where we write our functions and code blocks.

For example, a function that is written inside another function has access to the variables of the parent function despite where the function is invoked.

So the lexical scoping means that the scope is defined at the location where the variable or function is defined, and not where they run.

Let's check the below example to understand this.

const userName = "Abhishek";
function sayUserName() {
  console.log(userName);
}
function sayUserNameAgain() {
  const userName = "Anurag";
  // Invoke the first function
  sayUserName();
}
sayUserNameAgain(); // Abhishek

Let's see what has happened here:

When the sayUserNameAgain() function is called, it creates a local variable userName and sets its value as Sarah.

In the next line, the sayUserName() function is called, and sayUserName() function is defined outside the sayUserNameAgain() function.

sayUserName() function logs the userName variable, but userName is not defined in the sayUserName() scope. So we have to go up one scope to the global scope to get the value of userName which is Abhishek.

Even though we have userName = "Anurag" right above where the sayUserName() function invokes, we have never accessed that value.

This is because lexical scoping requires us to go where the functions are defined, not where they run.

I hope now you understand what lexical scope is. So let's move on to the scope chain.


⛓️2. Scope Chain

The scope chain is how Javascript looks for variables. When looking for variables through the nested scope, the inner scope first looks at its own scope. If the variable is not assigned locally, which is inside the inner function or block scope, then JavaScript will look at the outer scope of said function or block to find the variable. If Javascript could not find the variable in any of the outer scopes on the chain, it will throw a reference error.

Let's take an example and go through this process step by step. Check the below code.

// Global variable
const userName = "Abhishek";

// Outer function
function calcAge(birthyear) {
  const currentYear = 2022;
  const age = currentYear - birthyear;

   // inner block
  if (age <= 60) {
    var working = true;
    const message = `Abhishek is still employed!`;
    console.log(message);
  }

  // inner function
  function yearsToRetire() {
    const retirement = 60 - age;
    console.log(`${userName} will be retired in ${retirement} years!`);
  }

  yearsToRetire();
}

calcAge(2003);

In the above example,

  • We have a global variable called userName.

  • We have an outer function calcAge(), which is in the global scope.

  • We have an inner function, yearsToRetire(), nested inside calcAge() function.

  • Also, we have an if block inside the calcAge() function.

With the above example, let's try to understand how the scope chain works.

7.png

First, we have the global scope, which has only one variable, userName. There is a function declared in the global scope, which is calcAge(). But to keep things simple, let's focus on the variables. And keep in mind that function and variables work the same way in the scope chain.

9.png

If you remember, each function creates its own scope. So inside the global scope, the first function scope is created with the calcAge() function.

Inside the calcAge() function there are two variables declared, which are currentYear and age. Also, we have access to the global variable userName inside the calcAge() function.

If we have any need to access the variable userName inside this function, then JavaScript looks inside the calcAge() function to see whether the variable is declared inside the scope. When JavaScript can't find it there, it will reach out to the outer scope, that is the global scope.

10.png

Next, inside the first scope, there is a second function, yearsToRetire(), which also creates its own scope containing the retirement variable set to 60 - age. Now we have a nested structure of scopes with one scope inside the other.

We have a string that needs access to userName variable inside this function. Since JavaScript cannot find this variable within the local scope, it will look up in the scope chain until it finds the variable and uses it.

Also, inside this yearsToRetire() function scope we also have the access to variables inside the caclAge function scope, since caclAge is the parent scope and outer scope of yearsToRetire() function.

11.png

There is an if block inside the calcAge() function, which has the two variables declared inside that. However, as I explained earlier, the variable declared with var is not block scoped. So the variable working will be a part of the calcAge() function scope. Since the working is in the calcAge() function scope, the yearsToRetire() function scope also has access to it.

The scope chain applies to block scope as well. Therefore, the if block scope gets access to all the variables from its outer scope. So the block scope can access the variable inside the calcAge() function scope and global scope.

Another important thing to remember is that the if block scope does not have access to any variables in the yearsToRetire() function scope, and vice versa. The reason for this is lexical scoping.

The way we can access variables depends on where the scope is placed or where it is written in the code. In this scenario, none of these two scopes is written inside one another. We could say that they are sibling scopes since they are both child scopes of the calcAge() function scope. So, according to the lexical scoping, they cannot access each other's variables. Scope chain only works upwards, not sideways.

Screenshot 2022-09-12 103526.png

So this is how the scope chain works. 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 the 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.


🧵 2. Javascript is single threaded language.

🤔 What does it mean by Javascript is a single threaded language?

If you have been using Javascript for a while then you may come across the phrase that it’s a single threaded language.

What does that mean?

Javascript engine runs on a V8 engine 🚂 that has a memory heap and a call stack.

JS is a single threaded which means only one statement is executed at a time.

Before we dive into what it means why it runs on a single thread. I would want to first go over the terminology that will help you in understanding.

My attempt will be to explain in the simplest manner possible. To understand this better you need to know a Data Structure known as Stack (Last In, First Out).

Synchronous (or sync) execution usually refers to code executing in sequence. In sync programming, the program is executed line by line, one line at a time. Each time a function is called, the program execution waits until that function returns before continuing to the next line of code.

➡️ For example, you are calling someone and waiting for them to pick up so that you can talk to them. You’re not doing any other thing until they pick up the phone.

You fulfill the request sequentially.

const one() => {
     const two() => {
       console.log('4');
 }
    two();
}

So, what happens under the call stack?

Screenshot 2022-09-12 105506.png

The call stack job is to fill in the instructions and pop an instruction as it gets executed.

Javascript is a single threaded language that can be non-blocking. Single threaded means it has only one call stack. Whatever is on the top of the call stack it runs first.

In the above program, functions are running sequentially.

What if we have a function that is required to do the heavy lifting? Should we let the user wait till that process is over?

const one() {
      console.log("Hello");
}
const two () {
    for(i=0; i<= 10000000; i++){
}
const three(){
       console.log("World");
}
one();
two();
three();

Consider the above example, what if our second function has to loop through huge numbers? Does this mean three( ) has to wait till two() is executed? Technically, Yes!

In our small example, it may not mean much but if we have to implement it in a real project then the users may not be able to do anything until the first process is finished.

Asynchronous (or async) execution refers to execution that doesn’t run in the sequence it appears in the code. In async programming, the program doesn’t wait for the task to complete and can move on to the next task.

➡️To put it in example: You call someone and while you’re waiting for them to pick up the phone, you’re also running errands.

Different languages have different ways to implement asynchronous. The most popular is through Multi-threading.

JS can also be non-blocking and behave as if it were multi-threaded.

It means that it doesn't wait for the response of an API call, I/O events, etc., and can continue the code execution.

In brief, Java implements multi-threading by creating a child thread that does it’s own separate execution and then merges back with the parent thread.

This however can run into a problem known as Deadlock, which can be dealt with various deadlock prevention mechanisms.

Let us see from the below example, how asynchronous in JavaScript is implemented :

console.log('Abhishek');

setTimeout(() => {
    console.log('Raghav');    
}, 2500);

console.log('Nikhil');

Screenshot 2022-09-12 111525.png

In the above code, 'Raghav' gets printed after 'Nikhil' as 'Raghav' which may denote a very heavy process gets shifted to a timeout instruction.

In a nutshell, the asynchronous implementation in Javascript is done through a call stack, call back queue and Web API, and event loop.

Call stack job as we saw earlier is to check what instruction is at the top of the stack and execute it. If there is an instruction like setTimeout() that requires extra time to execute then call stack will pop that out and send it to Web API.

The job of the event loop is to continuously check if an event occurred, like a mouse click or keyboard stroke so that it can send that to the call stack. Of course, your mouse click will be given higher priority for execution than an image load.

In Javascript, All instructions are put on a call stack. When the stack arrives at setTimeout, the engine sees it as a Web API instruction and pops it out, and sends it to Web API. Once the Web API is done with the execution, it will arrive at the callback queue.

The engine checks if the call stack is empty. If it is empty, then we check the callback queue which has the instruction setTimeout in it. The callback queue sends it to the callback stack and the instruction is executed.

The above words seem like a bookish definition, let’s go deep down into these with the help of some practical examples.


📱 3. Execution Context and Call Stack

An execution context is a place where your javascript code is actually executed and the call stack is the collection of your execution context which works in the LIFO(Last in First out manner).

A) Execution Context ⬇️

let x = 10;
function squareOf(n) {
  return n * n;
}
let result = squareOf(x);

The code is straightforward:

x is assigned a value 10;

function declaration squareOf;

third, call the squareOf()and assigned it to the result ;

What is happening behind the scenes in javascript, how the javascript engine executes the following code??.. 🍴 When the javascript executes the above script it creates execution context and each execution context has two phases:

1. The Creation Phase:

2. The Execution Phase:

Whenever our script executes firstly it will create a global execution context and during the creation phase, it will perform the following task:

a. Creates a global object window

b. Creates a this object binding which points to the global object above.

c. Set memory space for functions and variables

d. Store the function declaration in memory heaps and variable declaration with undefined values in the global execution context

Global Object : window,

this: window,

x: undefined ,

squareOf: function {…}.

result : undefined

After the creation phase it will move to the execution phase, during this phase the code will be executed line by line:

Global Object: window,

this: window,

x: 10,

squareOf: function {…}.

result : squareOf()

Whenever a function call is there like result = product(), it will create a Function execution context,which is similar to a global execution context, but instead of creating a global object, it will create an arguments object which contains a reference to the parameters passed, the creation phase of the product will look like:

Global Object : window,

this: Global Object ,

z: undefined,

then the execution phase :

Global Object : window,

this: Global Object ,

z: 10,

But we are not getting our end result because we got a function call during the execution phase of the global execution context, so firstly the execution phase of the function execution context will be completed, then our global execution context. The whole thing is managed by the call stack.

So now let's understand what is Call stack.


B) Call Stack ⬇️

The call stack is based on the LIFO format i.e. last in first out, When you execute the script the javascript engine creates a global execution context and pushes it on the top of a stack.

Screenshot 2022-09-12 151003.png

main() : denotes the global execution context

Whenever there is a function call, then it creates a function execution context and pushes it on the top of a stack and the execution will shift to the top context of the stack.

Screenshot 2022-09-12 151500.png

Javascript engine will execute the execution context which is on the top of the stack and pop it out of the stack. It will continuously repeat the cycle until the stack is empty.

Screenshot 2022-09-12 151621.png

The final result will look like this after executing the whole script.


🚀 4. JavaScript Hoisting

In this article, you will also learn about JavaScript hoisting with the help of examples.

Hoisting in JavaScript is a behavior in which a function or a variable can be used before declaration.

Code example:

// using userName before declaring
console.log(userName);   // undefined
var userName;

The above program works and the output will be undefined. The above program behaves as

var userName;
console.log(userName);   // undefined

Since the variable test is only declared and has no value, an undefined value is assigned to it.

Note: In hoisting, though it seems that the declaration has moved up in the program, the actual thing that happens is that the function and variable declarations are added to memory during the compile phase.


✅ Variable Hoisting

In terms of variables and constants, keyword var is hoisted, and let and const do not allow hoisting.

Code example:

// program to display value
asiaCup = ''Shrilanka'';
console.log(asiaCup);
var asiaCup; // Shrilanka

In the above example, variable a is used before declaring it. And the program works and displays the output 5. The program behaves as:

// program to display value
var asiaCup;
asiaCup = ''Shrilanka'';
console.log(asiaCup); // Shrilanka

However, in JavaScript, initializations are not hoisted.

Code example:

// program to display value
console.log(isEnrolled);
var isEnrolled = true;

Output:

undefined

The above program behaves as:

var a;
console.log(a);
a = 5;

Only the declaration is moved to the memory in the compile phase. Hence, the value of variable a is undefined because a is printed without initializing it.


Also, when the variable is used inside the function, the variable is hoisted only to the top of the function.

Code example:

// program to display value
var a = 4;

function greet() {
    b = 'hello';
    console.log(b); // hello
    var b;
}

greet(); // hello
console.log(b);

Output:

hello

Uncaught ReferenceError: b is not defined

In the above example, variable b is hoisted to the top of the function greet and becomes a local variable. Hence b is only accessible inside the function. b does not become a global variable.

Note: In hoisting, the variable declaration is only accessible to the immediate scope.


If a variable is used with the let keyword, that variable is not hoisted.

Code example:

// program to display value
a = 5;
console.log(a);
let a; // error

Output:

Uncaught ReferenceError: Cannot access 'a' before initialization

While using let, the variable must be declared first.


✅ Function Hoisting

A function can be called before declaring it.

Code example:

// program to print the text
greet();

function greet() {
    console.log('Hi, Abhishek here.');
}

Output:

Hi, Abhishek here

In the above program, the function greet is called before declaring it and the program shows the output. This is due to hoisting.


However, when a function is used as an expression, an error occurs because only declarations are hoisted.

Code example:

// program to print the text
greet();

let greet = function() {
    console.log('Hi, there.');
}

Output:

Uncaught ReferenceError: greet is not defined

If var was used in the above program, the error would be:

Uncaught TypeError: greet is not a function


Note: Generally, hoisting is not performed in other programming languages like Python, C, C++, or Java.

Hoisting can cause undesirable outcomes in your program. And it is best to declare variables and functions first before using them and avoid hoisting.

In the case of variables, it is better to use let than var.


ook! 👍

I think I'm done now 🤗🤗

Keep reading 📑 Keep exploring 🤩 Keep growing 🚀

Thanks to all of you!

You can connect with me on:

👋 Instagram: @abhishekpatil7770

👋 LinkedIn: Abhishek Patil

👋 Findcoder: @abhishekpatil7487