Skip to main content

Command Palette

Search for a command to run...

JS Handbook: Fundamentals

Updated
17 min read

This blog is the very first blog of the series I'm going to be writing for understanding and preparing for JS interviews. Disclaimer I have given any Interviews related to JS yet, but have intensely researched on the common topics that are touched during an JS interview.

A beginner who has no idea about JS can also use this blog as a starting point to begin their journey in JS.

List of Topics going to be covered in this blog ↴
  • Single threaded nature of JS along with Execution Context & Call Stack.

  • Understanding variables and Data types

  • Javascript Operators: Basics you need to know

  • Control flow in JS: if, else, and switch explained

Single Threaded nature of JS

Javascript is a synchronous & single threaded programming language, which means that it can only execute one command at a time in the exact order it appears.

As you can see from the above example, how exactly is the code being read & executed by the main thread(single thread) from top to bottom, thats the order it follows to interpret the code.

Execution Context & Call Stack

To keep this section abit beginner friendly I will use a real world analogy, through which I was been taught. This section will be a little tough but bear with me as it will help u create a deeper understanding of different variable declarations as we will be discussing it ahead.

Imagine you are the onwer of the factory(the factory manufactures furniture made from different kind of wood). The first thing required to begin with the making of the furniture is raw material, in our case its the different kind of wood - relate these wood to different datatypes used in our programming which the core fundamental of anything to even begin with.

Now multiple operations are performed within a single factory itself, from Cutting&Sizing, Shaping&Machining, Sanding&Preparation, Assembly and Quality Analysis, these all operations are a separate entities within a big single factory, considering the fact that all these tasks are very different from each, for which each department(mini factory) will have a separate space to function within the greater single factory - relate this mini factories to function present inside your code file being the greater factory of all including all the functions.

Now for the factory and each of the mini-factories there must be a way to keep a track of what is completed and what needs to be completed, for that we create a separate bulletin board, one for the entire factory, and for every mini-factory accordingly, we can relate this (bulletin board where all the comlpleted tasks along with to do task are pinned) to -

A single Global Execution Context(GEC) having the context of the entire code, and an individual context for every function also know as Function/Local Execution Context(FEC).

Now to manage every process/task is being executed in an order, since we can't just assemble stuff and do the preapration everything is done in an order.

As we read above how the main thread analyses the code in a particular order, We need a taskManager who decide's how the tasks are performed - this is the job of Call Stack in Javascript

Code Example of how things exactly work ↴

var itemPrice = 50;

function calculateTotal(itemPrice) {
    var tax = 5;
    return tax + itemPrice;
}

// Function invoked!
var finalTotal = calculateTotal(itemPrice);

so var is one of the ways how you declare a variable in js, and accordingly the above example shows how a function is written.(this will be covered ahead in detail going forward)

When the script is executed a Global Execution Context is generated creating a global object called window, the main thread scans through the entire code file and accordingly allocates memory to declared variables and functions. This phase is known as creation/memory phase.

We will talk about the global object - window in the upcoming sections

As you can see above every variable has been allocated a memory, assigned as undefined. That's how the variable is stored inside the memory phase, whereas coming to the function it stores the entire body within the GEC's memory phase.

After the memory is allocated, the execution/code phase comes into the picture, the GEC is pushed to the Call Stack.

The code phase is responsible for assigning/re-assigning variables, control flow, execution of functions and evaluating expressions.

as you can during the code phase it reads line by line from the code file and assigns its respective values to every variable, along with that only when a function is invoked a separate context for it is generated which is pushed to the Call Stack on top of GEC (signifying that the main thread is currently working on function context) as the same operations performed on the GEC have been completed by FEC the returned value is respectively assigned to the finalTotal variable.

The FEC is popped from the call stack and we reach the End of Line exiting the execution and popping the GEC from the stack too.

Stack is empty, there is nothing left to execute - terminate the program.


Understanding variables and Data types

Declaring variables - var, let, const

So what are variables ?
In simple words variables acts as storage box, that store the value/information into the machine and act as reference through which can retrieve the desired values.

So now we have understood how does the JS engine exactly work( the Global Exec Context, Function Exec. Context, Call Stack) we can understand the 3 different ways to declare variables in JS var, let, const.

var num1 = 10; // num1 is attached to a global object called 'window'
let num2 = 20; // num2 is not attached to 'window'

console.log(window.num1); // 10
console.log(window.num2); // undefined

if(num2 > 30){
    var num3 = 10;
    let num4 = 20;
}

console.log(num3); // 10
console.log(num4); // undefined

The difference between var & (let, const) is Scope.

Function Scoped:

so when the code is scanned from top to bottom, whenever a variable is declared with var it's attached to the memory of its Execution Context - (Global Exec Context / Function Exec Context), it does not get restricted to the blocks of if-else conditionals / loops.

Block Scoped:

on the other hand whenever a variable is declared with let/const its attached to a seperate temporary memory just for that block.
block is the curly braces { } inside which the code is written as you can see in the above example for if statement.

"This behaviour of declaration of variables and functions moved to the top of their containing scope during the creation/memory phase is known as Hoisting"

age = 18;

function eligibleToVote(age){
    if(age >= 18) return true; 
    else return false;
}

var age;
var result = eligibleToVote(age);
console.log(result) // true

the above code will work fine as we learned that during the memory phase, the var variables are attached to the execution context and initialized with undefined, thus being hoisted so during the execution phase the age value gets updated.

age = 18;

function eligibleToVote(age){
    if(age >= 18) return true;
    else return false;
}

let age;
// ReferenceError: cannot access 'age' before initialization
let result = eligibleToVote(age) 
console.log(result) 

the above code throws an error why?

Its because of TDZ( Temporal Dead Zone ). What is TDZ?
During the memory phase unlike var, let and const are left un-initialized. The period between the start of the execution phase and the moment the engine actually reads the line where the variable is declared is called the Temporal Dead Zone, accessing an item before initialization results in ReferenceError.

Difference Between let and const :

re-assignment of variable declared using const is forbidden, and also it requires you to initialize during the time of declaration itself. There is a catch to when a const variable holds a reference type as objects or arrays, the internal properties and elements of the structure can still be modified. Where as you can re-assign let variable, along with declaration without initialization is possible for let variables

//not possible since it should inititialized at the moment of declaration
const name; 
name = "Mahin" 

const name = "mahin" // works fine ✔️
// won't work ❌ re-initialization of const variables is not alloed 
name = "yuji"

let age;
age = 20 //for let variables this way of declaration is accepted
age = 10 //age is re initialized to 10

const person = {
    name:"Mahin",
    age: 20
}

person = {} //not possible as declared using "const" but,
person.name = "yuji" //works completely fine as we are chaning the internal properties not the structure itself

let person1 = {
    name:"yuji",
    age: 20
}

person1 = {} //this will work since it was declared using "let"

Data-types - primitives & non-primitives

these are the raw materials(foundations of any language) because of which we are able to attribute data in different kinds of values from numbers to strings.

There are 2 kinds of data-types in js -

Primitives:

All the primitives are immutable and stored in a stack memory where each variable assigned with primitives have a seperate memory in the stack memory.

  1. Number : Integers and floating points, ex: 10, -21, 6.7, 9.00000. Note: it also includes NaN(Not a Number) as a number too.

  2. String : Textual data ex: "Javascript"

  3. Boolean : True/False

  4. undefined : a variable that has been declared but not initialized with a value.

  5. null : intentional absence of value for a declared variable

  6. Symbol : a unique and immutable identifier

  7. BigInt : Used for integers larger than the exsiting Number type ex: 86597856978509875n always write the letter n after completing the entire number to notify the machine to consider it as BigInt

let int = 67 // number type
let float = 6.7777 // number type

let largeInt = 98579056879056805978568709809n; //bigInt type

let str = "JS :)"
let age; //age is currently undefined
console.log(typeof age) //undefined

let emptyObj = null;
console.log(emptyObj) //object, this is an old bug of JS which couldn't be changed, since it might case hinderance to the legacy code.

let isEligibletoVote = true; //boolean type

let sym = Symbol("Mahin") // a unique identifier creater with label of "Mahin"

Non-Primitives :

Technically there is only 1 non-primitive datatype called object, which consist of internal properties/ elements, due to its dynamic nature it's not possible to store it in a stack memory, so these are stored in a pool of large memory called Heap memory, storing the reference pointer of the object in the Heap memory in the stack memory itself.

  1. Object : arrays, plain objects, functions, and Dates. These all are objects under the hood.
let obj = { //object having internal properties as "name" and "age"
    name: "Mahin",
    age: 20
}

let arr = [1,true,"JS",1.999,null]; // array can contain different types.

function callback(){ //declaring the function.
    return true;
}

callback(); //invoking(executing) the function.

After we have understood how the primitive and non-primitives are stored inside the memory, we can touch the topic of Pass by Value and Pass by Reference.

Pass by value means when a variable is assigned to another variable, it strictly copies the value of the variable its being assigned too. They are independent of each other occupying their own respective memory

let num1 = 10;

let num2 = num1; //only the value is copied from num1 to num2  

When an object is created, it is stored in a memory space, and the variable associated with it stores the memory address or reference pointer where the object is stored, this is know as Pass by Reference. (Below i have provided a more simler to understand diagram.


Javascript Operators: Basics you need to know

for a person encountering operators for the first time, in short these are symbols that help you perform different types of operations with the values you have got stored in the memory.

There exist different kind of operators from equality operators to logical operators and many more -

1. Equality Operators

  • Strict Equality (===): Checks both, the value and datatype whether they are equal or not. It performs no type conversions.

  • Loose Equality (==) : Checks for only the value, It also performs type conversions

let num = 10;
let strNum = "10";

if(num == strNum) //values are same after type conversion,irrespec. of type
    console.log(`num is equal to strNum`);

if(num === strNum) //false as the datatype are not same
    console.log(`num's value along with datatype match with strNum`)

I have a question for you too ↴

console.log(null == undefined) //result = ? think from the perspective of value only.

console.log(null === undefined) // result = ? no more hints.
console.log(false == "") // result = ?
console.log(false === "") // result = ?


//an unfamiliar type coercions
console.log([] == false) // results in true,as an empty array coerces to "" 

2. Comparison Operators

so the above discussed operators come inside the comparison operator itself, its just i wanted to emphasize more on those two equality operators, mentioned them seperately.

As the name suggests these operators are used to perform comparisons between the operands

Operators -> Less than ( < ), Greater than ( > ), Less than or Equals to ( <= ), Greater than or Equals to ( >= ), Not Equals to ( != ), Stricter version of Not Equals to ( !== ). The comparisons result return boolean value.

console.log(10 >= 10) //true
console.log(10 > 5) //false

console.log(10 <= 100) //true
console.log(100 < 100) //false

console.log("10" != 10) //false since its only checking the value.
console.log("10" !== 10) //true checking datatype + value.

3. Logical Operators

It's kind of funny that in Js logical operators do not always return true/false but the values of the operands too.

To understand these operators you need to know about Truthy & Falsy values -

Falsy values: 0, null, undefined, "" and NaN. Except these all values everything is Truthy in JS.

There are Two logical Operators:

AND Operator (&&): Evaluates from left to right. looks for the first falsy value and returns it.

OR Operator ( || ): Evaluates from left to right. looks for atleast one truthy value and returns it.

console.log(":)" || "") // :)
console.log(":)" && "") // ""

console.log(10 && null) // null

There is one more operator that was recently added in ES2020 called Nullish Coalescing (??), that returns the right hand side operand when the left-hand side operand is null or undefined, a safe check alternative of OR operator to prevent falsy values from being passed.

//its job is to strictly check for null or undefined exist or not, but acts like an OR operator itself
console.log(10 ?? "") // 10 acting as OR operator
console.log(null ?? 10) // 10 since LHS was null it will return RHS i.e. 10

console.log(undefined ?? ":)") // :)

4. Arithmetic Operators

these are the mathematical operators used to more arithmetics among operands.

Operators : + , -, /, % , (), ** - standard mathematical operators.

console.log(60 + 7) // 67
console.log(70 - 1) // 69

console.log(23 * 3) // 69
console.log((700/7) - 33) // 67
//the parenthesis is use for grouping giving higher priority to the operation inside of the parenthesis

//would highly encourage you to play with / and % as one returns quotient on operation and the other returns remainder

console.log(100/10) // 10 is the quotient
console.log(100%10) // 0 is the remainder 

//power operator ** ex: 2**3 === 2^3 => 8
console.log(2**3) // 8

5. Assignment Operator

these are the operators used to assign or/and perform an operation alongside on operands.

Operators: =, +=, -=, *=,/=. Standard arithmetic operators combined with =.

let num = 10
let sum = 0;

sum += num // is equivalent to sum = sum + num.

//similarly when performing assignment operators combined with arithmetic operators perform operation on the assigned operand along with the one to which its being assigned.

Control flow in JS: if, else, and switch explained

With the help of Control Statements the main thread's flow is decided about which block of code needs to be executed .

1. if , else-if and else statements:

Evaluates condition within the parenthesis coercisng the result of Truthy/Falsy value to a boolean value. and based on the result if true executes the code within the curly braces (having its seperate block scope)

you can also use these statements without curly braces but the catch is you can only write a line of command to execute more than that it wont consider within its control flow.

let num = 17;

//if one block is executed the rest of the blocks within the else-if ladder are discarded and we skip the current conditional statements attached to the conditional statement that was just executed in our case

//result -> the number is positive and we exit the control flow immediately
if(num > 0) { 
    console.log("the number is positive") 
}
else if(num < 0) {
    console.log("the number is negative");
}
else { 
    console.log("the number is equal to zero");
}

//result -> 
// 17
// nums is equal to 17 
if(num == 17) 
    console.log(num);
    console.log("num is equal to 17"); // the catch is that this line of code will always run irrespective of the control statement as its not considered within the block of the control flow rather its global scope.

// as only single line of command is accepted within the control flow when written without curly braces {}.
    

2. Switch Statements

this accepts a single value or expression result, and strictly evaluates using "===", so if we passed 1 as a string in the below example it would not be accepted by case 1.

let weekDayNumber = 1

switch (weekDayNumber) {
    case 1:
        console.log("monday")
        break;
    case 2:
        console.log("tuesday")
        break;
    case 3:
        console.log("wednesday")
        break;
    case 4:
        console.log("thursday")
        break;
    case 5:
        console.log("friday")
        break;
    case 6:
        console.log("saturday")
        break;
    case 7:
        console.log("sunday")
        break;
    default:
        break;
}

So the reason for using break statements within the switch case is to terminate the flow statement there itself rather than executing all the cases below it. for ex:

let weekDay = "sunday"

switch(weekDay){
    case "monday":
    case "tuesday":
    case "wednesday":
    case "thursday":
    case "friday":
    case "saturday":
    case "sunday":
        console.log("its a week day!");
        break;
    default: 
        break;
}

now in the above example we can see for the respective weekdays we want a singular result we intentionally fall-through by letting it execute the same result for all the above cases and end the flow using break statement after the case "sunday".

the default statement is used when none of the cases match the weekDay, thats what the thread resorts to when it has no where to go.

There is one Operator which acts as Conditonal flow statements called Ternary Operator.

Syntax: condition ? expressionIfTrue : expressionIfFalse

let num = -10;

let result = num > 0 ? "num is positive" : "num is negative"
console.log(result) // num is negative

JS Handbook

Part 1 of 3

this is just a digital form of my JS notes, that are important from Interview perspective, enenthough im just a college student, haven't given any interviews yet (disclaimer). But intensively researched on interview topics, so accordingly I prepared my notes.

Up next

JS Handbook: Functions are also an "object"

This article is the 3rd part of the JS Handbook Series, in which I'll be covering objects, functions and "this" keyword in depth. Only the topics that require deeper conceptual understanding will be c