JavaScript Interview Preparation Cheat Sheet

·

10 min read

  • Understanding Scope and Scope types in JavaScript

  • What does Single Threaded in JavaScript?

  • JavaScript: Call Stack Explained

  • What is Hoisting in JavaScript?

1 top.jpeg

Scope and Scope Chain are fundamental concepts of JavaScript and other programming languages. Yet, these concepts confuse many new JavaScript developers. The knowledge of these concepts is essential in mastering JavaScript.

Having a proper understanding of these concepts will help you to write better, more efficient and clean code. Which will, in turn, help you to become a better JavaScript developer.

So in this article, I will explain what scope and scope chain is, how JavaScript engine performs variable lookups and internals of these concepts.

What is Scope?

A scope in JavaScript determines the accessibility of variables, objects and function in your code during runtime. This essentially means availability of a variable in particular section of code is determined by the location of the variable declaration.

Why is Scope Important?

  • The main benefit of scope is security. That is, the variables can be accessed from only a certain area of the program. Using scope, we can avoid unintended modifications to the variables from other parts of the program.

  • The scope also reduces the namespace collisions. That is, we can use the same variable names in different scopes.

Types of Scope

  • Global Scope,
  • Function Scope, and,
  • Block Scope.

Global Scope

Any variable that’s not inside any function or block (a pair of curly braces), is inside the global scope. The variables in global scope can be accessed from anywhere in the program. For example:

var greeting = 'Hello World!';
function greet() {
  console.log(greeting);
}
// Prints 'Hello World!'
greet();

Local Scope or Function Scope

Variables declared inside a function is inside the local scope. They can only be accessed from within that function, that means they can’t be accessed from the outside code. For example:

function greet() {
  var greeting = 'Hello World!';
  console.log(greeting);
}
// Prints 'Hello World!'
greet();
// Uncaught ReferenceError: greeting is not defined
console.log(greeting);

Block Scope

ES6 introduced let and const variables, unlike var variables, they can be scoped to the nearest pair of curly braces. That means, they can’t be accessed from outside that pair of curly braces. For example:

{
  let greeting = 'Hello World!';
  var lang = 'English';
  console.log(greeting); // Prints 'Hello World!'
}
// Prints 'English'
console.log(lang);
// Uncaught ReferenceError: greeting is not defined
console.log(greeting);

We can see that var variables can be used outside the block, that is, var variables are not block scoped.

Nested Scope

Just like functions in JavaScript, a scope can be nested inside another scope. For example:

var name = 'Peter';
function greet() {
  var greeting = 'Hello';
  {
    let lang = 'English';
    console.log(`${lang}: ${greeting} ${name}`);
  }
}
greet();

Here we have 3 scopes nested within each other. First, the block scope (created due to the let variable) is nested inside the local or function scope which is in turn nested inside the global scope.

Lexical Scope

Lexical Scope (also known as Static Scope) literally means that scope is determined at the lexing time (generally referred to as compiling) rather than at runtime. For example:

let number = 42;
function printNumber() {
  console.log(number);
}
function log() {
  let number = 54;
  printNumber();
}
// Prints 42
log();

Here the console.log(number) will always print 42 no matter from where the function printNumber() is called. This is different from languages with the dynamic scope where the console.log(number) will print different value depending on from where the function printNumber() is called.

If the above code was written in a language that supports dynamic scoping, the console.log(number) would have printed 54 instead.

Using lexical scope we can determine the scope of the variable just by looking at the source code. Whereas in the case of dynamic scoping the scope can’t be determined until the code is executed.

Most of the programming languages support lexical or static scope such as C, C++, Java, JavaScript. Perl supports both static and dynamic scoping.

Conclusion

So in a nutshell, a scope is an area where a variable is visible and accessible. Just like functions, scopes in JavaScript can be nested and the JavaScript engine traverses the scope chain to find the variables used in the program.

# What does Single Threaded in JavaScript?

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

image.png

JavaScript is a single-threaded language. This means it has one call stack and one memory heap. As expected, it executes code in order and must finish executing a piece code before moving onto the next. So if we print from 1to 5 it will execute every line one by one and can’t execute all prints at a single time.

console.log(1);
console.log(2);
console.log(3);
console.log(4);
console.log(5);

Execute line 1 & Print => 1
Execute line 2 & Print => 2
Execute line 3 & Print => 3
Execute line 4 & Print => 4
Execute line 5 & Print => 5

JavaScript: Call Stack Explained

image.png

What is Call Stack?

The call stack is a Last-In, First-Out (LIFO) data structure containing the address at which execution will resume and often local variables and parameters from each call. Let’s understand the call stack by a simple example.

image.png

When we step into a function, we put something on to the stack, if we return from a function, we pop off the top of the stack that’s all the stack can do. Let’s run this code line by line and check how it exactly works.

Step 1

If you run this file, there’s kind of the main function, right, like the file itself, so, we push that on to the stack. Then we have some function definitions, they’re which are not gonna execute right away, and finally, we have our print square, right, so the print square is a function call, so we push “printSquare” onto the stack.

image.png

Step 2

After putting “printSquare” into the stack, it called the “square” function so we put the square into the stack.

image.png

Step 3

Again “square” function called “multiply”, So we put that also into the stack. We are not popping anything from the stack because till now there is nothing returned, One function is calling another function as a return statement and the stack is getting filled.

image.png

Step 4

After all these function callings, now we get a return statement.

return a * b;

So we popped up the stack and remove “multiply” from the stack.

image.png

Step 5

Now we got the result of the return statement from the “square” function. So we remove it also from the stack.

image.png

Step 6

Now after successfully executing the “square” function we have a statement to execute.

console.log(squared);

It prints the result of the “square” function assigned to the “squared” variable. Now our stack looks like this.

image.png

Step 7

After execution of this “console.log” it will also be popped up from the stack leaving “printSquare(4)” & “main()” function alone :(.

image.png

Step 7

Now finally after completing all the rituals and steps of the program “printSquare(4)” will be popped up from the stack and then “main()” function. Now again our stack is empty and alone :(.

image.png

What is Stack trace?

Did you ever try to understand the error logs shown in the browser console? Sometimes we ignore just by reading the first line but if we look carefully into it, they are very helpful to tell from where the error originates and what are the consequences of the error. Let’s see this example.

image.png

Here baz() is calling a function bar(), bar() is calling a function foo() & our dear foo() throwing error. Now if we look into the logs of the browser it is giving us an excellent tracking of the error and how it originates. That's what we call “Stack Trace”.

What is Stack Overflow?

No, we aren’t talking about the famous stackoverflow.com website but a term generally used in the v8 engine and JavaScript. To understand when and why stack overflow occurs. Take a look at the next image.

image.png

Here we are calling foo() function which is again calling foo() function. Let’s take a look at what will happen to our stack.

image.png

After some time, our Chrome says “well we are not expecting you to call the same function 16K times so we are upset with you and that's why I am giving an error”.

image.png

So that's how to call stack works. In the upcoming articles, we are going to cover the event loop, callback queue, working of the V8 Engine, and how NodeJS works on the V8 engine.

# What is Hoisting in JavaScript?

JavaScript Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables or classes to the top of their scope, prior to execution of the code.

Hoisting allows functions to be safely used in code before they are declared.

Variable and class declarations are also hoisted, so they too can be referenced before they are declared. Note that doing so can lead to unexpected errors, and is not generally recommended.

Function hoisting

One of the advantages of hoisting is that it lets you use a function before you declare it in your code.

catName("Tiger");

function catName(name) {
  console.log(`My cat's name is ${name}`);
}
/*
The result of the code above is: "My cat's name is Tiger"
*/

Without hoisting you would have to write the same code like this:

function catName(name) {
  console.log(`My cat's name is ${name}`);
}

catName("Tiger");
/*
The result of the code above is the same: "My cat's name is Tiger"
*/

Variable hoisting

Hoisting works with variables too, so you can use a variable in code before it is declared and/or initialized.

However, JavaScript only hoists declarations, not initializations! This means that initialization doesn't happen until the associated line of code is executed, even if the variable was originally initialized then declared, or declared and initialized in the same line.

Until that point in the execution is reached the variable has its default initialization (undefined for a variable declared using var, otherwise uninitialized).

Note: Conceptually variable hoisting is often presented as the interpreter "splitting variable declaration and initialization, and moving (just) the declarations to the top of the code".

Below are some examples showing what can happen if you use a variable before it is declared.

var hoisting

Here we declare and then initialize the value of a var after using it. The default initialization of the var is undefined.

console.log(num); // Returns 'undefined' from hoisted var declaration (not 6)
var num; // Declaration
num = 6; // Initialization
console.log(num); // Returns 6 after the line with initialization is executed.

The same thing happens if we declare and initialize the variable in the same line.

console.log(num); // Returns 'undefined' from hoisted var declaration (not 6)
var num = 6; // Initialization and declaration.
console.log(num); // Returns 6 after the line with initialization is executed.

If we forget the declaration altogether (and only initialize the value) the variable isn't hoisted. Trying to read the variable before it is initialized results in a ReferenceError exception.

console.log(num); // Throws ReferenceError exception - the interpreter doesn't know about `num`.
num = 6; // Initialization

Note however that initialization also causes declaration (if not already declared). The code snippet below will work, because even though it isn't hoisted, the variable is initialized and effectively declared before it is used.

a = "Cran"; // Initialize a
b = "berry"; // Initialize b

console.log(`${a}${b}`); // 'Cranberry'

let and const hoisting

Variables declared with let and const are also hoisted but, unlike var, are not initialized with a default value. An exception will be thrown if a variable declared with let or const is read before it is initialized.

console.log(num); // Throws ReferenceError exception as the variable value is uninitialized
let num = 6; // Initialization

Note that it is the order in which code is executed that matters, not the order in which it is written in the source file. The code will succeed provided the line that initializes the variable is executed before any line that reads it.

class hoisting

Classes defined using a class declaration are hoisted, which means that JavaScript has a reference to the class. However the class is not initialized by default, so any code that uses it before the line in which it is initialized is executed will throw a ReferenceError.

Function and class expression hoisting

Function expressions and class expressions are not hoisted.

The expressions evaluate to a function or class (respectively). They are typically then assigned to a variable or passed to other functions. In this case, the variable declaration is hoisted and the expression is its initialization. Therefore the expressions are not evaluated until the relevant line is executed.