JavaScript interview prep Cheatsheet

JavaScript interview prep Cheatsheet

·

9 min read

Table of contents

No heading

No headings in the article.

Scoping

What does Scoping in JavaScript Mean? Yes, this question is very important to be asked while learning JavaScript and now with in two points I say

  1. Every variable defined in JavaScript has a scope.
  2. Scope determines whether the variable is accessible at a certain point or not.

Q) So now why do we need Scoping?

Ans) Scoping makes our code secure in terms that user has access to data what is necessary at that time. Also it helps to reduce bugs in program and also solve variable name resolution problem when we have two variable with same name but in different scope.

Types of Scope The current context of execution. Scope determines the visibility of variables. In JavaScript there are 3 types of scopes, namely:

  1. Block scope

  2. Function scope

  3. Global scope

Block Scope

Prior to ES6, we only had two types of scopes, function and local scope. ES6 introduced two new keywords let, and const. These two keywords provide block scoping in JavaScript. In block scoping, all variables declared inside a { } block cannot be accessed outside the block.

{
    const name = "Hemanth";
    let age = 24;
}
// 'name' and 'age' are not available here outside the { }

This above cannot be practiced using var

{
    var name = "Hemanth"
}
// 'name' can be accessed from here that is outside the { } also

Function scope

All variables declared inside the function, cannot be accessed outside that function. This means that the variable have local scope as they are local to that function.

function example() {
    let name = "Manish";
}
// 'name' cannot be accessed from here

This will allow us to declare a new variable with same name outside the function.

function example() {
    let name = "Manish";
}
let name = "Rohan";

All function (local) variables are created when they are declared and deleted when the function is completed.

NOTE: Function arguments (parameters) act as local variables inside the function.

Global scope

Variables that are declared outside the function have global scope. That is, they are available anywhere in the JS program.

var age = 20;

function example() {
    console.log(20);  // 20
}

Any variable that is assigned a value without declaring it, is automatically a global variable. However, this is not possible while in ‘strict mode’.

function example() {
    name = "Rahul";
}

console.log(name);

In a web browser, all global variables are deleted when you close the browser window or tab. With JavaScript, global scope is the JavaScript file itself. In HTML, a variable is said to have global scope when it is in the window object.

Global variables must be defined with a var keyword for it to be included in the window object.

var age = 20;
console.log(window.age);  // 20

let age = 45;
console.log(window.age);  // undefined

NOTE: It is advised not to create global variables as your global variable can overwrite your window variable and your local variables can overwrite your global variables. This can lead to a lot of confusion and inefficient code.

Single Threaded

JavaScript is known to be single threaded because of its property of having only one call stack, which some other programming languages have multiple. JavaScript functions are executed on the call stack, by LIFO (Last In First Out). For example we have a piece of code like this:

const foo = () => {
  const bar = () => {
    console.trace();
  }
  bar();
}

foo();

And the call stack will have foo to enter into call stack, then bar.

threaded.png

After bar() is done, it will be popped off from the call stack, followed by foo(). You will see an anonymous function underneath when printing out the stack trace, and that is the global execution context of main thread.

Thread1.png

This seems to be logical as JavaScript is a single threaded language and there is only a single flow to execute all these functions. However, in the case that we are having some unpredictable or heavy tasks in the flow (for example making an API call), we do not want them to block the execution of the remaining codes (else users might be staring at a frozen screen). This is where the asynchronous JavaScript comes in.

Callstack

The call stack is primarily used for function invocation (call). Since the call stack is single, function(s) execution, is done, one at a time, from top to bottom. It means the call stack is synchronous. At the most basic level, a call stack is a data structure that uses the Last In, First Out (LIFO) principle to temporarily store and manage function invocation (call).

Let’s break down our definition:

LIFO: When we say that the call stack, operates by the data structure principle of Last In, First Out, it means that the last function that gets pushed into the stack is the first to be pop out, when the function returns.

Let us take a look at a code sample to demonstrate LIFO by printing a stack trace error to the console.

function firstFunction(){
  throw new Error('Stack Trace Error');
}

function secondFunction(){
  firstFunction();
}

function thirdFunction(){
  secondFunction();
}

thirdFunction();

When the code is run, we get an error. A stack is printed showing how the functions are stack on top each other. Take a look at the diagram.

call.png

You will notice that the arrangement of the functions as a stack begins with the firstFunction() (which is the last function that got into the stack, and is popped out to throw the error), followed by the secondFunction(), and then the thirdFunction() (which is the first function that gets pushed into the stack when the code is executed).

Temporarily store: When a function is invoked (called), the function, its parameters, and variables are pushed into the call stack to form a stack frame. This stack frame is a memory location in the stack. The memory is cleared when the function returns as it is pop out of the stack.

call2.png

Manage function invocation (call): The call stack maintains a record of the position of each stack frame. It knows the next function to be executed (and will remove it after execution). This is what makes code execution in JavaScript synchronous.

Think of yourself standing on a queue, in a grocery store cash point. You can only be attended to after the person in front of you have been attended to. That’s synchronous.

This is what we mean by “manage function invocation”.

How does the call stack handle function calls? We will answer this question by looking at a sample code of a function that calls another function. Here is the example code:

function firstFunction(){
  console.log("Hello from firstFunction");
}

function secondFunction(){
  firstFunction();
  console.log("The end from secondFunction");
}

secondFunction();

call3.png

This is what happens when the code is run:

  1. When secondFunction() gets executed, an empty stack frame is created. It is the main (anonymous) entry point of the program.
  2. secondFunction() then calls firstFunction()which is pushed into the stack.
  3. firstFunction() returns and prints “Hello from firstFunction” to the console.
  4. firstFunction() is pop off the stack.
  5. The execution order then move to secondFunction().
  6. secondFunction() returns and print “The end from secondFunction” to the console.
  7. secondFunction() is pop off the stack, clearing the memory.

What causes a stack overflow? A stack overflow occurs when there is a recursive function (a function that calls itself) without an exit point. The browser (hosting environment) has a maximum stack call that it can accomodate before throwing a stack error.

Here is an example:

function callMyself(){
  callMyself();
}

callMyself();

The callMyself() will run until the browser throws a “Maximum call size exceeded”. And that is a stack overflow.

call4.png

Hoisting

Hoisting is when the JavaScript engine moves variables and function declarations to the top of their respective scope before code execution.

Example of Hoisting variable declaration:

x = 6
var x;
console.log(x) 
// x would be equal to 6 with no errors thrown

Naturally, you would think this code would not work because you can't assign a variable and then have the declaration below that right?

Well, you could do just that with var because it will initialize variables before declaration, allowing JavaScript to hoist it to the top of the function and not throw any errors.

Example of Hoisting in function declaration:

dogBreed('Beagle')

function dogBreed(name) {
  console.log('My favorite dog breed is the ' + name + '!') 
}
// My favorite dog breed is the Beagle!

Although this code runs just fine, it is best practice to avoid invoking functions before declaring them for readability.

Mixing these two together can lead to very confusing code. Take this code snippet for example:

var dogBreed = "Pug";
var favDogBreed = function () {
  console.log("My favorite dog breed: " + dogBreed);
    var dogBreed = "Shih Tzu";
      console.log("Bob's favorite dog breed: " + dogBreed);
};

favDogBreed();
// Original favorite dog: undefined
// New favorite dog: Shih Tzu

Confusingly enough, JavaScript initialized the first dogBreed to undefined because of hoisting and then starts executing the function which is why we got this result.

Variables that are initialized in JavaScript are not hoisted though. Interestingly enough, the code snippet below prints out NaN:

console.log(x + y)
var x = 4
var y = 2
// Output is NaN

This happens because JavaScript is hoisting x and y but not their respective initializations = 4 and = 2 resulting in NaN because x and y are not numbers!

Thankfully in 2015, we got the ES6 revision of JavaScript and that introduced let and const which effectively outdates var.

Lets take another look favDogBreed but declare everything with let instead of var:

let dogBreed = "Pug";
let favDogBreed = function () {
  console.log("My favorite dog breed: " + dogBreed);
    let dogBreed = "Shih Tzu";
      console.log("Bob's favorite dog breed: " + dogBreed);
};

favDogBreed();
// This will throw a ReferenceError and not let this code run

All is now right in the world and the engineer will have to go and fix their confusing code.

However just because errors are thrown now, does not mean that hoisting is patched out of Javascript. It still occurs with variables declared with let and const but they are never initialized. Take this code snippet for example:

dogName = "Sparky";
const dogName
console.log(dogName)
// This code doesn't run

dogName = "Sparky";
let dogName
console.log(dogName)
// This results in a ReferenceError

What does this mean? For starters, hoisting is still happening with these variables declared with let and const but there are some differences.

const refuses run because of syntax error. Same thing happening with let, but JavaScript sees the variable and is aware of it, but JavaScript cannot use the variable until it has been properly declared.

When declared with let, our variable dogName is in this phenomenon called the temporal deadzone. The temporal deadzone does not apply to var because JavaScript initializes the variable to undefined when it is accessed before the declaration.

The best practices to avoid hoisting issues is to:

  • Declare variables and functions let and const where ever you can
  • Always declare variables at the beginning of each and every scope because this is how JavaScript and developers read code -Never ever use var

Finally we arrive at the end of this article. Hope you had a great time reading it.