JavaScript Basics: Variables, Scope, and Hoisting
JavaScript Basics: Variables, Scope, and Hoisting
JavaScript is a Dynamic,Single-threaded ,Interpreted (code executed line by line by an interpreters) Programming language with First class Functions ,It is most well known scripting
language for Web pages ,also many non-browser environment also use it like [Link] .
JavaScript is a dynamically typed Language ie, variable types are determined at runtime so we don't want to specify data type
First class Functions : Functions that are treated like any other variables in a programming language called first class functions.
JavaScript Engines
JavaScript engines are the programs or interpreters that execute JavaScript code. examples:
1. V8 (Chrome,[Link],Opera)
2. SpiderMonkey (Firefox)
3. JavaScriptCore (Safari,Webkit)
4. Chakra (Microsoft Edge)
FOUNDATIONS
VARIABLES
Variables are containers for storing data [Link] can declare a variable with var, let or const keywords.
let myVariable;
SCOPE
In JavaScript Scope refers to the context in which variables and functions are accessible.
JS has two main type of scope , Global Scope and Local Scope
Global Scope
Variable declared outside any function or block are in the global [Link] can be accessed from anywhere in the code.
function accessGlobal() {
[Link](globalVariable); // Accessible
}
accessGlobal();
[Link](globalVariable); // Accessible
Local Scope
Local scope refers to variables that are accessible only with in specific part of the code , such as function or block;
Function Scope
Variables declared inside a function using var, let, or const are in function scope. They are not accessible outside the function.
function myFunction() {
var functionScoped = "I'm inside a function";
[Link](functionScoped); // Accessible
}
myFunction();
[Link](functionScoped); // Not accessible, ReferenceError
Block Scope
Variables declared inside a block (e.g., inside {}) using let or const are block-scoped. They are accessible only within that block.
if (true) {
let blockScoped = "I'm inside a block";
[Link](blockScoped); // Accessible
}
Lexical Scope
Lexical scope refers to the ability of a function to access variables from its outer (enclosing) functions. The scope of a variable is determined by its position in the code, not by where it
is used..
function outerFunction() {
var outerVariable = "I'm outside!";
function innerFunction() {
[Link](outerVariable); // Accessible due to lexical scope
}
innerFunction();
}
outerFunction();
LEXICAL ENVIRONMENT
A lexical environment in JavaScript refers to the structure that holds variables, function declarations, and the scope in which they exist..
Scope Chain
When a variable is referenced, JavaScript looks up the variable name in the current scope. If it's not found, it moves to the outer scope, and so on, until it reaches the global scope.
This chain of scopes is called the scope chain.
Shadowing
When a variable in a local scope has the same name as a variable in an outer scope, the local variable "shadows" the outer one. The inner variable takes precedence within its scope.
var shadowVar = "Global";
function shadowTest() {
var shadowVar = "Local";
[Link](shadowVar); // Logs "Local"
}
shadowTest();
[Link](shadowVar); // Logs "Global"
Illegal Shadowing
If you create a variable in a global scope with the let keyword and another variable with the var keyword in a block scope but the exact same name, it will throw an error. This is called
illegal shadowing
if (true) {
var name = "kumar";
[Link](name);
}
[Link](name);
VARIABLE HOISTING
When variables are hoisted, only the declarations are hoisted to the top, not the initializations. If you try to access a variable before it is initialized, it will be undefined.
var hoistedVar;
[Link](hoistedVar); // Output: undefined
hoistedVar = "I am hoisted";
[Link](hoistedVar); // Output: "I am hoisted"
FUNCTION HOISTING
Learn functions in JavaScript first for a better understanding, then come back.
Function declaration are fully hoisted meaning name and it's body are moved to the top of their containing scope. This allow you to call function before its declaration
function hoistedFunction() {
[Link]("I am hoisted");
}
Function expressions, including those created with var, let, or const, are not hoisted in the same way. Only the variable declaration is hoisted, not the function assignment.
var hoistedExpression;
hoistedExpression(); // TypeError: hoistedExpression is not a function
hoistedExpression = function () {
[Link]("I am not hoisted");
};
LET
let is block-scoped, meaning it is only accessible within the block it is declared in (e.g., within ).
Variables declared with let are hoisted but not initialized, leading to a Temporal Dead Zone (TDZ) until the declaration is encountered
Variables declared with let cannot be re-declared within the same scope.
CONST
const is block-scoped, similar to let.
Variables declared with const are hoisted but not initialized, leading to a Temporal Dead Zone (TDZ) until the declaration is encountered.
Variables declared with const cannot be re-declared within the same scope.
const requires an initial value at the time of declaration, and the variable reference cannot be reassigned. However, the contents of objects and arrays declared with const can
be modified.
DATA TYPES IN JS
PRIMITIVE DATA TYPES
Primitive data types are most basic data types, They are immutable meaning cant change values after they are created
let person = {
name: "John",
age: 30,
};
2. Array Represents an ordered collection of values (indexed). Example: let numbers = [1, 2, 3, 4, 5];
function greet() {
return "Hello";
}
4. Date Represents a single moment in time. Example: let now = new Date();
5. RegExp Represents regular expressions, used for pattern matching within strings Example: let pattern = /ab+c/;
try {
throw new Error("Something went wrong");
} catch (e) {
[Link]([Link]); // 'Something went wrong'
}
notdefined means The variable has not been declared in the current scoped this leads into reference errors
Nan
In JS Nan stands for "Not a Number" , and means by computational results cant be expressed as a meaningful Number
isNan function -- this function convert the value to a number and checks if it is a Nan
In strict mode, defining a variable without using the var, let, or const keywords will throw an error, preventing accidental creation of global variables.
"use strict";
a=10;
[Link](a);
[Link]('hello');
without strict mode the above code will print 10 following hello
OPERATORS
ARITHMETIC OPERATORS
Arithmetic operators are used to perform mathematical operations.
ASSIGNMENT OPERATORS
Assignment operators are used to assign values to variables.
let x = 5;
x += 3; // 8
COMPARISON OPERATORS
Comparison operators are used to compare two values and return a boolean result.
LOGICAL OPERATORS
Logical operators are used to combine or invert boolean values
Nullish Coalescing (??): returns the right-hand side operand when the left-hand side operand is null or undefined
ex:
BITWISE OPERATORS
Bitwise operators perform operations on the binary representation of numbers
AND (&): Performs a bitwise AND.
5 | 1; // 5 (0101 | 0001)
5 ^ 1; // 4 (0101 ^ 0001)
Unsigned Right Shift (>>>): Shifts bits to the right, filling with zeros.
5 >>> 1; // 2 (0101 >>> 1 = 0010)
STRING OPERATORS
In addition to the arithmetic addition operator, +, which concatenates strings, JavaScript also provides other string-related operations.
TYPE OPERATORS
typeof: Returns the type of a variable.
typeof 5; // "number"
CONDITIONAL STATEMENTS
Conditional statements allow you to perform different actions based on different conditions.
1. IF ELSE Statements
The if...else statement allows you to execute code based on a condition. You can chain multiple conditions using else if
let num = 5;
2. SWITCH Statements
The switch statement is another way to handle multiple conditions. It's useful when you have many conditions based on the same variable.
let fruit = "apple";
switch (fruit) {
case "banana":
[Link]("Banana is yellow");
break;
case "apple":
[Link]("Apple is red or green");
break;
case "orange":
[Link]("Orange is orange");
break;
default:
[Link]("Unknown fruit");
}
LOOPS
Loops allow you to repeat a block of code multiple times.
1. FOR Loop
The for loop is the most common type of loop in JavaScript
let i = 0;
while (i < 5) {
[Link]("Iteration number " + i);
i++;
}
3. DO WHILE LOOP
The do...while loop is similar to the while loop, but it guarantees that the block of code is executed at least once, even if the condition is false.
let j = 0;
do {
[Link]("Iteration number " + j);
j++;
} while (j < 5);
4. FOR IN LOOP
The for...in loop is used to iterate over the properties of an object.
let person = { name: "John", age: 30, city: "New York" };
//output will be
// name: John
// age: 30
// city: New York
in case of array , for in loop will iterate through the index of the array ,
ex:
5. FOR OF LOOP
The for...of loop is used to iterate over iterable objects like arrays, strings, and Node Lists.
ERRORS in JS
In errors are categorized into different types , each represent specific kind of problems occurring during the execution of the code The main Types are
Syntax Errors - Occur when code does not conform to the syntax of the language
Reference Errors - Occur when try to reference a variable that does not exist or initialized in the current scope
Type Error -Occur when the value is not a expected type
Range Error Occur when a value is not with in the set or range of allowed values , ex: passing an invalid length to array like -1
CLOSURE in JS
Closure is the combination of function bundled together with references to its lexical environment .
This means that the function has access to variables and functions defined in its outer scope, even after the outer scope has finished executing
ex:
function x() {
var a = 7;
function y() {
[Link](a);
}
return y;
}
var z = x();
z(); // This will output 7
STRINGS
JavaScript are sequences of characters enclosed within quotation marks. They can be defined using either single quotes (') or double quotes (").
STRING METHODS
1. charAt(index)
Returns the character at the specified index
let str = "Hello, World!";
[Link]([Link](0)); // "H"
2. includes(searchString, position)
Checks if a string contains another string.
[Link]([Link]("World")); // true
3. indexOf(searchValue, fromIndex)
Returns the index of the first occurrence of a specified value.
[Link]([Link]("o")); // 4
4. replace(searchValue, newValue)
Replaces a specified value with another value in a string.
5. trim()
Removes whitespace from both ends of a string.
let strWithSpaces = " Hello, World! ";
[Link]([Link]()); // "Hello, World!"
6. toUpperCase()
Converts a string to uppercase.
7. toLowerCase()
Converts a string to lowercase.
8. split()
Splits a string into an array of substrings based on a specified separator.
9. slice()
Extracts a part of a string and returns a new string.
let str = "hello world";
let substring = [Link](6);
[Link](substring); // "world"
10. substring()
Similar to slice(), but handles negative indices differently.
// Using slice()
let substring1 = [Link](6, 11); // "world"
let substring2 = [Link](-5, -1); // "world"
// Using substring()
let substring3 = [Link](6, 11); // "world"
let substring4 = [Link](-5, -1); // "world" (same as substring3)
FUNCTIONS IN JS
A JavaScript function is a block of code designed to perform a particular [Link] encapsulates a set of instructions that can be reused throughout a program. Functions can take
parameters, execute statements, and return values, enabling code organization, modularity, and reusability in JavaScript programming.
Function Declaration
A function declaration defines a named function in JavaScript
function greet() {
[Link]("Hello!");
}
greet(); // "Hello!"
Function Expressions
A function expression is when a function is assigned to a variable. This contrasts with function declarations, which are standalone functions
Key Differences:
Hoisting: Function declarations are hoisted, meaning they can be called before they are defined in the code. Function expressions are not hoisted.
Naming: Function declarations must have a name, whereas function expressions can be anonymous.
ARROW FUNCTION
An arrow function expression is a compact alternative to a traditional function expression, with some semantic differences and deliberate limitations in usage
const functionName = (parameter1, parameter2, ...) => {
// function body
};
One of the most significant features of arrow functions is that they do not have their own this context. Instead, they inherit this from the parent scope at the time they are defined
(Lexical scoping)
Arrow functions cannot be used as constructors. You cannot create new objects using the new keyword with an arrow function.
function Person() {
[Link] = 0;
setInterval(() => {
[Link]++;
[Link]([Link]);
}, 1000);
}
function multiply(x, y) {
return x * y;
}
Eval Functions
The Eval function in JS is able to read strings which must be an expression or formula . It basically reads and evaluate if tng might be a calculation In strict mode, declaring a variable
named eval or re-assigning eval is a SyntaxError example:
[Link](eval("2 + 2"));
// Expected output: 4
Pure Functions
Its is a function that does not have side effects and a function that return exact same thing every time that function invoked with same input
function numberToString(num) {
return [Link]();
}
(function () {
var a = 3;
[Link](3);
})();
Generator Functions
Regular function only return only once ,Generator function can return multiple values , one after another on demand To create generator function use special syntax function*
function* generatorFunction(a,b){
yield a+b;
yield a-b ;
}
[Link]([Link]().value);
[Link]([Link]().value);
[Link]([Link]());
[Link]([Link]());
[Link]([Link]());
[Link]([Link]());
output will be
Factory Function
Its just a function create an object and return that object , They offer a flexible way to create objects without using class or new. They can encapsulate private variables and functions.
function createPerson(name, age) {
return {
name: name,
age: age,
greet: function () {
[Link](
`Hello, my name is ${[Link]} and I am ${[Link]} years old.`
);
},
};
}
CONSTRUCTOR FUNCTION
Constructor function in Java helps to create multiple instance of objects ,When you use the new keyword with a constructor function, it creates a new object, sets the this context to the
new object, and allows you to add properties and methods to that object.
// Constructor function
function Person(name, age) {
[Link] = name;
[Link] = age;
[Link] = function () {
[Link]("Hello, my name is " + [Link]);
};
}
// Creating instances
const person1 = new Person("Alice", 30);
const person2 = new Person("Bob", 25);
Default parameters
Default parameters in JavaScript allow you to specify default values for function parameters. If a caller does not provide a value for a parameter, the function uses the default value
instead. This feature simplifies function definitions and helps avoid undefined or null values.
ex:
function createUser(name = "Anonymous", age = 18) {
return `Name: ${name}, Age: ${age}`;
}
Prototype
In JavaScript, the prototype property is used to add properties and methods to a constructor function's prototype. This allows all instances created by the constructor function to share
the same methods, which is more memory-efficient and helps with code organization.
When you add methods to a constructor function's prototype, all instances of that constructor will have access to those methods, but the methods themselves are not duplicated for
each instance. Instead, they are shared through the prototype chain.
example :
// Constructor function
// Creating instances
const person1 = new Person("Alice", 30);
const person2 = new Person("Bob", 25);
Inheritance In JS
Inheritance in JavaScript is a concept that allows one object to inherit properties and methods from another object.
Prototype-Based Inheritance
In JavaScript, every object has a prototype property (except [Link]). When you access a property or method on an object, JavaScript will first look for it on that object. If it
doesn't find it, it will look for it on the object's prototype. If it still doesn't find it, it will continue up the prototype chain until it reaches [Link]. If the property or method isn't
found anywhere in the chain, undefined is returned.
// Parent constructor function
function Animal(name) {
[Link] = name;
}
[Link] = function () {
[Link]([Link] + " makes a sound.");
};
// Create instances
const dog1 = new Dog("Buddy", "Golden Retriever");
[Link](); // Output: Buddy makes a sound.
[Link](); // Output: Buddy barks.
OBJECTS
Objects is one of the data types in JS , it used to store values in key:value pairs , Objects are mutable in JS
example:
const person = {
firstName: "John",
lastName: "Doe",
id: 5566,
fullName: function () {
return [Link] + " " + [Link];
},
};
Objects can also create using new keyword. We can access object properties in tow ways
1. [Link]
2. objectName["propertyName"]
delete [Link] ;
Object Methods
Object methods are actions that can performed on objects
There are several methods to convert an object into array, the main advantage of this , this process make an object iterable(ie we can loop through this)
const obj = {
cat: "meow",
dog: "bow bow",
cow: "meh...",
};
1. [Link] Using [Link] and passing object will return a array with that object keys
example:
[Link]([Link](obj));
// returns ["cat","dog","cow"]
2. [Link] Using [Link] and passing object will return a array with that object values
example :
[Link]([Link](obj);
//returns ["meow", "bow bow","meh.."]
)
3. [Link] Using [Link] and passing object will return an 2D Array with key and of that Object
example:
[Link]([Link](obj))
//returns [["cat","meow"],
["dog","bow bow"],
["cow","meh..."]
]
4. [Link] Using [Link] makes an object immutable , ie you can't add, delete, or modify the properties of object . freezing is a one way operation , once a object
is frozen cannot unfrozen
const obj = {
a: 1,
b: 2,
nested: {
c: 3,
},
};
[Link](obj);
obj.a = 3; // No effect
delete obj.b; // No effect
obj.c = 4; // No effect
[Link](obj); // { a: 1, b: 2 }
[Link]([Link](obj)); // true
freezing is shallow , it affects the object itself not nested objects ie,
5. [Link] [Link] is less restrictive than freezing it prevents adding or removing properties , but allows modifying existing property values
const obj = {
a: 1,
b: 2,
nested: {
c: 3,
},
};
[Link](obj);
obj.a = 6; // Allowed
delete obj.b; // No effect
obj.c = 8; // No effect
[Link](obj); // { a: 6, b: 2 }
[Link]([Link](obj)); // true
Sealing is also shallow. It only affects the object itself, not nested objects.
This Keyword
In JS this keyword is reference to something like object. but this keyword work differently in different scenarios , like check in case of an object with normal function
let user ={
name= 'Aswanth',
age:26,
function getName(){
[Link]([Link])
}
}
[Link]() //print Aswanth in console
let user ={
name= 'Aswanth',
age:26,
childObj:{
nickname:"Ash"
function getName(){
[Link]([Link] ,"and",[Link])
}
}
}
[Link]() //print 'undefined and Ash' in console
in this case the this in referenced only to childObj , ie incase of normal function this only referenced its direct parent
this keyword value in arrow function referenced its par parent function , in above case there is no parent function so it point to the window object , to access name in this case need
to use nested arrow function
let user = {
name: "Aswanth",
age: 24,
getName() {
const nestedArrow = () => [Link]([Link]); //Aswanth
nestedArrow();
},
};
Consider an object
let name = {
firstname: "Aswanth",
lastname: "Ck",
printFullname: function () {
[Link]([Link] + " " + [Link]);
},
};
Now, if we need to create a similar object with the same properties (firstname, lastname) and printFullname function, instead of repeatedly writing the function in the new
object, we can borrow the printFullname function directly from the name object.
the syntax is
let name2 = {
firstname: "Yuvraj",
lastname: "Singh",
};
[Link](name2);
Usually, we don't store these types of functions inside objects. Instead, we make them global and use them accordingly:
let printFullname = function () {
[Link]([Link] + " " + [Link]);
};
[Link](name);
You can add more parameters to this function. When doing so, always pass arguments after the object:
[Link](name, "Kannur");
2. APPLY
The apply method works similarly to call. The only difference is that instead of passing arguments individually in the apply method, you pass them as an array.
let printFullname = function (hometown, state, country) {
[Link](
[Link] +
" " +
[Link] +
" from " +
hometown +
", " +
state +
", " +
country
);
};
3. BIND
The bind method creates a copy of the function and binds it with the object. It doesn't invoke the function directly; instead, it stores the function in a variable so you can call it
later.
let printFullname = function (hometown) {
[Link]([Link] + " " + [Link] + " from " + hometown);
};
CURRYING
Currying transforms a function that takes multiple arguments into a series of functions that each take a single argument.
Normal Function:
function multiply(a, b) {
return a * b;
}
Curried Function:
function multiply(a) {
return function (b) {
return a * b;
};
}
const multiplyBy2 = multiply(2); // This creates a new function that multiplies its argument by 2
[Link](multiplyBy2(3)); // Outputs: 6
CLASSES
In JavaScript Classes are the blue print to create an object
A class is defined using the class keyword followed by the class name. The class body is enclosed in curly braces {}. The constructor method in a JavaScript class is a special
method used for creating and initializing objects created with a class. When you create a new instance of a class, the constructor method is automatically called.
class Person {
// Constructor method to initialize the object
constructor(name, age) {
[Link] = name;
[Link] = age;
}
Inheritance
Classes can inherit from other classes using the extends keyword. This allows a class to inherit properties and methods from another class.
class Animal {
constructor(name) {
[Link] = name;
}
speak() {
[Link](`${[Link]} makes a sound.`);
}
}
bark() {
[Link](`${[Link]} barks.`);
}
}
get name() {
return this._name;
}
set name(newName) {
this._name = newName;
}
get age() {
return this._age;
}
set age(newAge) {
if (newAge > 0) {
this._age = newAge;
} else {
[Link]("Age must be positive");
}
}
}
constructor(name, age) {
this.#name = name;
this.#age = age;
}
get name() {
return this.#name;
}
get age() {
return this.#age;
}
}
ARRAYS
You can create an array in a few different ways:
[Link]("Date");
[Link](fruits); // Output: ["Apple", "Blueberry", "Cherry", "Date"]
2. pop()
Removes the last element from an array and returns that element.
3. unshift()
Adds one or more elements to the beginning of an array and returns the new length of the array.
[Link]("Apricot");
[Link](fruits); // Output: ["Apricot", "Apple", "Blueberry", "Cherry"]
4. shift()
Removes the first element from an array and returns that element.
5. length
Returns the number of elements in an array.
[Link]([Link]); // Output: 3
6. forEach()
Creates a new array with the results of calling a function for every array element.
8. map()
Creates a new array with the results of calling a function for every array element.
9. filter()
Creates a new array with all elements that pass a test implemented by a function.
10. reduce()
Executes a reducer function on each element of the array, resulting in a single output value.
11. sort()
Sorts the elements of an array in place and returns the sorted array. By default, it sorts elements as strings.
[Link]();
[Link](fruits); // Output: ["Apple", "Banana", "Cherry"]
12. indexOf()
Returns the first index at which a given element can be found in the array, or -1 if it is not present.
13. reverse()
Reverses the order of the elements in an array in place.
[Link]();
[Link](fruits); // Output: ["Cherry", "Banana", "Apple"]
14. slice()
Returns a shallow copy of a portion of an array into a new array. It does not modify the original array [Link](start, end)
14. splice()
Changes the contents of an array by removing, replacing, or adding elements. It modifies the original array [Link](start, deleteCount, item1, item2,
...)
REST OPERATOR
The rest operator collects multiple elements into a single array. It's also represented by three dots ( ... ) but is used in function [Link] operator collects elements into an array.
DESTRUCTURING
OBJECT DESTRUCTURING
Object destructuring is a syntax that allows you to extract properties from an object and assign them to variables with the same name. It's a convenient way to unpack values from
objects.
const person = {
firstName: "John",
lastName: "Doe",
age: 30,
};
[Link](first); // Output: 1
[Link](second); // Output: 2
[Link](third); // Output: 3
DEEP COPY
Creates a completely independent copy of the original object, including all nested objects and arrays. Changes to the copy do not affect the original. Deep copies are safer but more
expensive in terms of performance
[Link](1);
[Link](2);
[Link](2); // Duplicate value, will not be added
[Link](set); // Set { 1, 2 }
[Link]([Link](1)); // true
[Link]([Link]); // 2
[Link](1);
[Link](set); // Set { 2 }
[Link]();
[Link](set); // Set {}
WEAK SET
A WeakSet is similar to a Set, but it can only contain objects, and the references to the objects are weak. This means that if there are no other references to an object stored in the
WeakSet, it can be garbage collected.
[Link](obj1);
[Link](obj2);
[Link]([Link](obj1)); // true
[Link]([Link](obj2)); // true
[Link](obj1);
[Link]([Link](obj1)); // false
MAP
A Map is a collection of key-value pairs where keys can be of any type, including objects, functions, and primitives.
[Link]("name", "Alice");
[Link]("age", 25);
[Link]([Link]("name")); // Alice
[Link]([Link]("age")); // true
[Link]([Link]); // 2
[Link]("age");
[Link]([Link]("age")); // false
[Link]();
[Link]([Link]); // 0
WEAK MAP
A WeakMap is a collection of key-value pairs where the keys are weakly referenced, and must be objects.
[Link](obj1, "Developer");
[Link](obj2, "Designer");
[Link]([Link](obj1)); // Developer
[Link]([Link](obj2)); // true
[Link](obj1);
[Link]([Link](obj1)); // false
setTimeout(function () {
let data = { name: "Alice", age: 30 };
callback(data);
}, 2000); // 2-second delay
}
Callback functions are commonly used in JavaScript for handling asynchronous operations, event handling, and more.
CALLBACK HELL
Callback hell, also known as "pyramid of doom," occurs when you have multiple nested callbacks in asynchronous code, making it difficult to read and maintain. This situation often
arises when dealing with asynchronous operations like file I/O, network requests, or timers.
doSomething(function (result1) {
doSomethingElse(result1, function (result2) {
doAnotherThing(result2, function (result3) {
doSomethingMore(result3, function (result4) {
// This nesting can go on...
[Link](result4);
});
});
});
});
INVERSION OF CONTROL
Inversion of control in JS is function or method does not control the flow of execution directly. Instead, it delegates control to another function that is passed as an argument. This is
commonly seen in asynchronous programming and event handling.
1. Memory Allocation phase (Variable environment) In this phase variable and functions stored as key value pairs
2. Code execution phase (Thread of Execution) In this phase javascript run code by line by line
CALL STACK
The call stack in JavaScript is a mechanism for managing the execution context of code. It keeps track of function calls, allowing the JavaScript engine to know what function is
currently being executed and what function called it. When a function is called, a new execution context is created and pushed onto the stack. When the function completes, its
execution context is popped off the stack.
WEB API
Functions like setTimeout, fetch, console, and event listeners are provided by the browser and run in the background. These APIs are not part of the JavaScript runtime but
are available in the environment (browser or [Link]).
CALLBACK QUEUE
When an asynchronous operation completes, its callback function is placed in the callback queue.
EVENT LOOP
This is a constantly running process that checks the call stack and the callback queue. If the call stack is empty, the event loop moves the first function from the callback queue to the
call stack to be executed.
MICRO TASK
Micro tasks are high priority tasks like Promise Callback , and MutationObserver Callbacks Microtasks include:
PROMISES
A promise in JavaScript is an object that represents the eventual completion or failure of an asynchronous operation and its resulting value. It provides a cleaner and more manageable
way to handle asynchronous tasks compared to traditional callbacks. A Promise contain 3 states
Creating a Promise:
const myPromise = new Promise((resolve, reject) => {
// Simulate an asynchronous task
setTimeout(() => {
const success = true; // Change to false to simulate a failure
if (success) {
resolve("Operation was successful!");
} else {
reject("Operation failed.");
}
}, 2000);
});
using a Promise
myPromise
.then((result) => {
[Link](result); // Logs "Operation was successful!" if resolved
})
.catch((error) => {
[Link](error); // Logs "Operation failed." if rejected
});
Promises help manage asynchronous operations by providing .then() for success, .catch() for errors, and .finally() for cleanup.
PROMISE CHAINING
Promise chaining is a technique in JavaScript where multiple promises are linked together in a sequence, with each promise starting after the previous one has been fulfilled. This is
accomplished by returning a new promise from the .then() method, which allows subsequent .then() methods to handle the new promise's fulfillment or rejection. This makes handling a
sequence of asynchronous operations more manageable and readable.
When you call .then() on a promise, it returns a new promise that resolves with the value returned from the .then() callback or rejects with an error thrown in the .then() callback.
[Link] .then() waits for the previous promise: The next .then() in the chain will wait for the previous promise to settle (either resolve or reject) before executing.
// Simulating asynchronous operations using promises
function firstAsyncTask() {
return new Promise((resolve, reject) => {
setTimeout(() => {
[Link]("First task completed");
resolve("First result");
}, 1000);
});
}
function secondAsyncTask(firstResult) {
return new Promise((resolve, reject) => {
setTimeout(() => {
[Link]("Second task completed with:", firstResult);
resolve("Second result");
}, 1000);
});
}
function thirdAsyncTask(secondResult) {
return new Promise((resolve, reject) => {
setTimeout(() => {
[Link]("Third task completed with:", secondResult);
resolve("Third result");
}, 1000);
});
}
// Chaining promises
firstAsyncTask()
.then((result) => {
return secondAsyncTask(result);
})
.then((result) => {
return thirdAsyncTask(result);
})
.then((result) => {
[Link]("All tasks completed with:", result);
})
.catch((error) => {
[Link]("An error occurred:", error);
});
Explanation:
firstAsyncTask: This function returns a promise that resolves after 1 second. When resolved, it logs a message and returns "First result".
secondAsyncTask: This function takes the result of firstAsyncTask as an argument and returns a promise that resolves after another second. When resolved, it logs a
message with the result from firstAsyncTask and returns "Second result".
thirdAsyncTask: This function takes the result of secondAsyncTask as an argument and returns a promise that resolves after another second. When resolved, it logs a
message with the result from secondAsyncTask and returns "Third result".
The promises are chained using .then() methods, ensuring each task is executed sequentially. The final .then() logs the final result after all tasks are completed. If any of the
promises reject, the .catch() method will handle the error.
in case of Error () : If any promise in the iterable is rejected, [Link]() returns a single promise that rejects with the reason of the first promise that was rejected.
This API is useful when you want to know the result of each promise, whether it was fulfilled or rejected, without stopping at the first rejection.
[Link]()
[Link]() returns a promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects, with the value or reason from that promise.
This is useful when you want to proceed with the first settled promise and don't care about the rest.
const promise1 = new Promise((resolve) => setTimeout(resolve, 500, "First"));
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, "Second"));
[Link]()
[Link]() takes an iterable of promises and returns a single promise that resolves as soon as any of the promises in the iterable fulfills, with the value of the fulfilled promise. If all
the promises are rejected, it returns a promise that is rejected with an AggregateError, which is an array of all the rejection reasons.
This is useful when you need the first successfully resolved value, ignoring any rejections.
async Functions
An async function is a function declared with the async keyword. This makes the function automatically return a promise, even if it doesn't explicitly return a promise. If the function
returns a value, the promise resolves with that value. If the function throws an error, the promise is rejected with that error.
fetchData();
fetchData();
POLYFILLS
A polyfill is a piece of code that provides a feature in older browsers or environments that do not natively support it.
In simpler terms, it's like a patch or workaround that lets you use modern JavaScript features in older browsers.
return result;
};
}
// Usage of filterEvenNumbers
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
Now You can call filterEvenNumbers directly on any array to filter out only the even numbers .
JSON
JSON stands for JavaScript Object Notation. It's a lightweight data-interchange format that's easy for humans to read and write, and easy for machines to parse and generate JSON is
based on two structures:
{
"name": "John Doe",
"age": 30,
"city": "New York",
"hobbies": ["reading", "coding", "traveling"]
}
JavaScript provides built-in methods to convert between JSON and JavaScript objects:
DOM MANIPULATION
The DOM is a hierarchical representation of a web page, where each element, such as headers, paragraphs, links, and images, is a node in this structure. By manipulating the DOM,
you can dynamically change the content, structure, and style of a web page.
3. Modifying Attributes:
[Link]('attribute', 'value'): Sets a new attribute or changes the value of an existing attribute.
[Link]('attribute'): Gets the value of an attribute.
[Link]('attribute'): Removes an attribute from an element.
4. Changing Styles:
6. Removing Elements:
EVENTS
Events in JavaScript are actions or occurrences that happen in the browser, such as clicking a button, pressing a key, or loading a page. These events can trigger specific functions,
allowing you to create interactive web experiences
EVENT PROPAGATION
Event propagation is how events travel through the DOM tree. When an event is triggered on a particular element, it doesn't just stay on that element; it can move up or down the DOM
tree. There are two main phases of event propagation:
1. Capturing Phase (Event Capture): The event starts from the root of the DOM tree and travels down to the target element. This phase is less commonly used but can be useful
for intercepting events before they reach their target.
2. Bubbling Phase (Event Bubble): After the event reaches the target element, it starts bubbling back up to the root, triggering the same event on each ancestor element. Event
bubbling is the more common phase in JavaScript event handling.
[Link](): Stops the event from bubbling up or capturing down the DOM tree.
Event Delegation
Event delegation is a technique where you attach a single event listener to a parent element instead of multiple listeners to each child element. This is useful when you have many
elements, like a list of items, and want to handle events for all of them.
Type Casting
Type casting in JavaScript refers to the process of converting a value from one data type to another.
ex:
Here number 3 is implicitly converted to a string and concatenated with '5', resulting in '53'.
ex:
let str = '456';
let num = Number(str);
[Link](num); // Output: 456
MEMOIZATION
Memoization is an optimization technique used to improve the performance of functions by caching their results. It avoids redundant calculations by storing previously computed values
and reusing them when the same inputs occur again.
2. Check Cache: On subsequent calls with the same arguments, the function checks the cache for a stored result instead of recalculating it.
3. Return Cached Value: If a cached result is found, it is returned immediately. If not, the function computes the result, stores it in the cache, and then returns it.
BENEFITS
Performance Improvement: Reduces the time complexity of functions with repetitive calculations by avoiding redundant work
Efficiency: Especially useful for functions with expensive or time-consuming operations, such as recursive algorithms or API calls
ex:
Memoization for a function that calculates the Fibonacci sequence
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = [Link](args);
if ([Link](key)) {
return [Link](key);
}
const result = fn(...args);
[Link](key, result);
return result;
};
}
ex:
// Set a new timer that will execute 'mainFunction' after the specified delay
timer = setTimeout(() => {
mainFunction(...args);
}, delay);
};
};
Benefits of Debouncing
Performance Improvement: Reduces the number of function executions by grouping multiple rapid triggers into a single execution.
Resource Efficiency: Helps avoid unnecessary computations or API calls, improving overall application performance.
THROTTLING
Throttling is a technique that limits how often a function can be called in a given period of time. It is useful for improving the performance and responsiveness of web pages that have
event listeners that trigger heavy or expensive operations, such as animations, scrolling, resizing, fetching data, etc.
Throttling can improve the performance and user experience of web pages by reducing the number of unnecessary or redundant operations. It can also prevent some issues such as:
Overloading the server or the browser with too many requests or calculations
Exceeding the rate limits or quotas of APIs or services
Wasting bandwidth or resources on operations that are not visible or relevant to the user
Creating janky or laggy animations or interactions
ex:
// Define a function that fetches some data from an API
function fetchData() {
[Link]("Fetching data...");
// Simulate an API call with a random delay
setTimeout(() => {
[Link]("Data fetched!");
}, [Link]() * 1000);
}
// Add an event listener to the window scroll event that calls the throttledFetchData function
[Link]("scroll", throttledFetchData);
Fetching data from an API or a database when the user scrolls, resizes, or types
Updating or animating elements on the page when the user scrolls, resizes, or moves the mouse
Logging or tracking user actions or events when they occur frequently
##Lazy loading
Please also refer to these topics to master JavaScript also practice practice more... ,
Composition
Promisify
Decorators
Starvation in JavaScript
Memory Heap