JAVASCRIPT - JavaScript is a programming language used to create dynamic content for websites
Is Java a scripting language? No Java is Not A Scripting Language its a Programming Language. For example, in the normal case, you have to compile a C program before you can run it. But in the normal case, you don't have to compile a JavaScript program before you run it
The event loop is a mechanism that allows JavaScript to perform non-blocking operations by offloading operations to the system, using a callback queue.
What are higher-order functions?
- Answer: Higher-order functions are functions that can take other functions as arguments or return functions as their results.
function higherOrderFunction(callback) {
callback();
}
higherOrderFunction(() => {
console.log("Hello from the callback!");
});
What are modules in JavaScript?
Modules are reusable pieces of code that can export functions, objects, or primitives from one file and import them into another.
// module.js
export const PI = 3.14;
// main.js
import { PI } from './module.js';
console.log(PI); // 3.14
let a = 2;
console.log(a * 2); // 4
console.log(a ** 3); // power of 8
console.log({} == {}); // false
console.log({} === {}); // false // object is non primitive so both reference are different
What is the difference between null
and undefined
?
- Answer:
null
is an assignment value representing the intentional absence of any object value, while undefined
means a variable has been declared but has not yet been assigned a value.
let a;
console.log(a); // undefined
let b = null;
console.log(b); // null
What are template literals?
- Answer: Template literals are a way to create strings in JavaScript using backticks (
`
). They allow for embedded expressions and multi-line strings.
const name = "Alice";
const greeting = `Hello, ${name}!`;
console.log(greeting); // "Hello, Alice!"
a shallow copy only copies the top-level properties of an object. If the object contains nested objects or arrays, a shallow copy will not create independent copies of those nested objects or arrays; instead, it will only copy references to them
Summary:
- Shallow copy: Copies only the top-level properties and shares references for nested objects.
- Deep copy: Copies all levels of an object, creating independent copies for nested objects.
let a = { name: "stephen" };
let z = {...a};
z.name = "prakash";
console.log(a.name); // stephen
Currying is the process of transforming a function that takes multiple arguments into a sequence of functions that each take a single argument
const curryFunction = (a , d) => (b) => (c) => {
return (a * b * c) + d
}
console.log(curryFunction(2, 1)(3)(5)); // 30
What is the purpose of localStorage
and sessionStorage
?
- Answer: Both
localStorage
and sessionStorage
are part of the Web Storage API. localStorage
stores data with no expiration date, while sessionStorage
stores data for one session and is cleared when the page session ends.
localStorage.setItem('username', 'Alice');
console.log(localStorage.getItem('username')); // "Alice"
sessionStorage.setItem('sessionUser', 'Bob');
console.log(sessionStorage.getItem('sessionUser')); // "Bob"
A callback is a function that is passed as an argument to another function and is executed after a certain event occurs or a task is completed.
Explain the purpose of async
and await
.
- Answer:
async
and await
are used to handle asynchronous operations in a more readable way. An async
function always returns a promise, and await
can be used inside an async
function to pause execution until the promise resolves.
Basic Math Methods
Math.abs(): Returns the absolute (positive) value of a number.
Math.round(): Rounds a number to the nearest integer.
Math.round(4.5); // 5
Math.round(4.4); // 4
Math.floor(): Rounds a number down to the nearest integer (towards negative infinity).
Math.ceil(): Rounds a number up to the nearest integer (towards positive infinity).
Math.trunc(): Removes the fractional part of a number, effectively truncating it towards zero
Math.sign(): Returns the sign of a number, indicating whether the number is positive, negative, or zero.
1
for positive numbers-1
for negative numbers0
for zero
Math.sign(10); // 1
Math.sign(-10); // -1
Math.sign(0); // 0
// Promises are objects are used to handle the asynchronous operation
// they provide then() method to handle fulfilled promises
// and a catch() method to handle rejections.
const handleTimeOutPromises = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('DONE.....');
}, 2000);
})
}
// handleTimeOutPromises().then((response) => { console.log(response) }).catch((error) => { console.log(error , 'error') });
const handleTimeOutFailurePromises = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('ERROR');
}, 2000);
})
}
// handleTimeOutFailurePromises().then((response) => { console.log(response) }).catch((error) => { console.log(error , 'error') });
Promise.all([handleTimeOutPromises(),handleTimeOutFailurePromises()]).then((response) => { console.log(response) }).catch((error) => { console.log(error , 'error') });
// Explanation:
// In the example above, Promise.all waits for handleTimeOutPromises, handleTimeOutFailurePromises to resolve.
// Once all are resolved, it returns a single promise that resolves with an array of the results.
// If any promise rejects, Promise.all immediately rejects with the reason of the first promise that rejected.
// Promise.race takes an iterable of promises and returns a single promise that resolves or rejects as soon as the first promise in the iterable resolves or rejects.
// Use Case: When you need the result of the first completed promise, regardless of whether it was resolved or rejected.
const promise1 = new Promise((resolve, reject) => { setTimeout(resolve, 500, 'one'); });
const promise2 = new Promise((resolve, reject) => { setTimeout(resolve, 100, 'two'); });
Promise.race([promise1, promise2]).then((value) => { console.log(value); }).catch((error) => { console.error(error); });// Output: "two"
// Promise.allSettled takes an iterable of promises and returns a single promise that resolves after all of the promises have settled, meaning each promise has either resolved or rejected. It never rejects, and it returns an array of objects describing the outcome of each promise.
// Use Case: When you want to know the result of all promises, whether they resolved or rejected, without short-circuiting on the first failure.
Promise.allSettled([handleTimeOutPromises(), handleTimeOutFailurePromises()]).then((results) => {
console.log(results, 'results');
});
// in javascript, closures are the inner function can access the outer function's variable
// its lexical scope
// They are commonly used for data encapsulation and maintaining state between function calls
// Closures are a fundamental concept in JavaScript
function outerFunction() {
let outerVariable = "Hey i am outside";
function innerFunction() {
return outerVariable; // Closures are The inner function can access the outer function's variable
}
return innerFunction();
}
const closures = outerFunction();
console.log(closures) // Hey i am outside
// What are the different data types in JavaScript?
// Answer: JavaScript supports the following data types:
// Primitive types: undefined, null, boolean, number, string, symbol, bigint.
// Non-primitive type: object (includes arrays, functions, and objects).
// Primitive Data Types
// ========================
// 1. undefined - Represents a variable that has been declared but not assigned a value.
let x;
console.log(x); // undefined
console.log(typeof x); // undefined
// 2.null - Represents the intentional absence of any object value.
let y = null;
console.log(y);
console.log(typeof y); // object
// 3.Boolean - Represents a logical entity and can have two values: true or false.
let isJavascriptFun = true;
console.log(isJavascriptFun);
console.log(typeof isJavascriptFun) // boolean
// 4. Number - Represents both integer and floating-point numbers.
let age = 25;
let price = 99.99;
console.log(age); // Output: 25
console.log(price); // Output: 99.99
// 5.String - Represents a sequence of characters, used to store and manipulate text.
let greeting = "Hello, World!";
console.log(greeting); // Output: "Hello, World!"
// 6.Symbol - Represents a unique identifier. Each Symbol is unique and immutable.
let sym = Symbol('id');
console.log(sym); // Symbol(id)
console.log(typeof sym); // symbol
// 7. BigInt - It is used for very large numbers that are beyond the safe integer limit in JavaScript
let bigNumber = BigInt(1234567890123456789012345678901234567890n);
console.log(bigNumber); // Output: 1234567890123456789012345678901234567890n
// Non-primitive type
// 1.Object
let person = {
name: "John",
age: 30,
isStudent: false
};
console.log(person.name); // Output: "John"
// 2.Array
let fruits = ["Apple", "Banana", "Cherry"];
console.log(fruits[1]); // Output: "Banana"
// 3.Function - A block of code designed to perform a particular task. Functions are objects in JavaScript.
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // Output: 5
// 4.Date - A built-in object used to work with dates and times.
let today = new Date();
console.log(today); // Output: current date and time
// Examples of Type Conversion
// JavaScript is a loosely typed or dynamic language,
// which means you don't have to specify the data type of a variable.
// It also allows type conversion, where a value of one type can be converted to another.
// 1.Implicit Conversion:
let num = "5" + 2;
console.log(num); // Output: "52" (String)
// 2.Explicit Conversion:
let num2 = "5";
let convertedNum = Number(num);
console.log(convertedNum); // Output: 5 (Number)
// var is function-scoped and can be redeclared or updated.
// let is block-scoped and can be updated but not redeclared in the same scope.
// const is block-scoped, and cannot be updated or redeclared once assigned.
// 1.var Example
// Scope: Function-scoped or globally-scoped.
// Hoisting: Variables declared with var are hoisted to the top of their scope but initialized with undefined.
// Re declaration: Can be re declared and updated.
function exampleVar() {
if (true) {
var message = "Hello"
}
console.log(message); // Hello
}
exampleVar();
var x = 10;
var x = 20; // Re declaration allowed
console.log(x); // Output: 20
// let Example
// Scope: Block-scoped.
// Hoisting: Variables declared with let are hoisted but not initialized (temporal dead zone).
// Re declaration: Cannot be re declared in the same scope, but can be updated.
function exampleLet() {
if (true) {
let message = "Hello"
}
// console.log(message) // ReferenceError: message is not define
}
exampleLet();
let y = 10;
y = 20; // Update allowed
console.log(y); // Output: 20
// let y = 30; // Error: Identifier 'y' has already been declared
// const Example
// Scope: Block-scoped.
// Hoisting: Variables declared with const are hoisted but not initialized (temporal dead zone).
// Re declaration: Cannot be re declared or updated in the same scope. Must be initialized at the time of declaration.
function exampleConst() {
if (true) {
const message = "Hello"
}
// console.log(message) // ReferenceError: message is not define
}
exampleConst();
const z = 10
// z = 20 // TypeError: Assignment to constant variable.
// const z = 30; // Error: Identifier 'z' has already been declared
console.log(z);
// An IIFE is a function that runs as soon as it is defined.
// It is written by placing the function inside parentheses followed by another set of parentheses to invoke it.
console.log((function welcomeMessage() {
return "Hello"
})()) // Hello
// Hoisting is a behavior in JavaScript where variable and function declarations are moved to the top of
// their containing scope during the compile phase, before code execution.
// Example 1: Variable Hoisting with var
console.log(a) // undefined
var a = 10;
console.log(a); // 10
var b;
console.log(b); // Output: undefined (because `a` is declared but not yet initialized)
b = 10; // Initialization happens here
console.log(b); // Output: 10
// Example 2: Function Hoisting
sayHello(); // Hello, world!
function sayHello() {
console.log("Hello, world!");
}
// In this example, the function sayHello is called before it is defined in the code.
// Due to hoisting, the entire function declaration is moved to the top of its scope,
// so the function can be called before it appears in the code.
// Example 3: Hoisting with let and const
// Variables declared with let and const are also hoisted, but they are not initialized.
//console.log(c) // ReferenceError: Cannot access 'c' before initialization
let c = 10
//console.log(d); // ReferenceError: Cannot access 'c' before initialization
const d = 30;
// Summary
// Variable hoisting with var: Variables declared with var are hoisted
and initialized with undefined.
// Function hoisting: Entire function declarations are hoisted,
allowing functions to be called before they are defined in the code.
// Hoisting with let and const: Variables are hoisted but are not initialized,
and accessing them before their declaration results in a ReferenceError
due to the temporal dead zone.
function debounce(func, delay) {
let timeoutId;
return function (...args) {
console.log('Debounced function triggered');
// Clear the previous timeout if the function is called again within the delay
clearTimeout(timeoutId);
// Set a new timeout to execute the function after the delay
timeoutId = setTimeout(() => {
console.log('Executing debounced function');
func.apply(this, args);
}, delay);
};
}
console.log(debounce())
===========================================================================
// How to Clone an Object in JavaScript?
// Example Object
const userDetails = {
name: "Stephen Prakash G",
age: 28,
verified: false,
hobbies: ["reading", "coding"]
};
// Method 1: Spread Operator (Shallow Clone)
const clone1 = { ...userDetails };
// Method 2: Object.assign() (Shallow Clone)
const clone2 = Object.assign({}, userDetails);
// Method 3: JSON.parse() and JSON.stringify() (Deep Clone)
const clone3 = JSON.parse(JSON.stringify(userDetails));
// Modifying clones to demonstrate immutability
clone1.name = "John Doe";
clone2.age = 30;
clone3.verified = true;
clone1.hobbies.push("gardening"); // This should not modify the original array due to shallow copy
// Output the clones and original object to see changes
console.log("Original Object:");
console.log(userDetails);
console.log("\nClones:");
console.log(clone1);
console.log(clone2);
console.log(clone3);
/*
Output:
Original Object:
{
name: 'Stephen Prakash G',
age: 28,
verified: false,
hobbies: [ 'reading', 'coding' ]
}
Clones:
{
name: 'John Doe',
age: 28,
verified: false,
hobbies: [ 'reading', 'coding', 'gardening' ]
}
{
name: 'Stephen Prakash G',
age: 30,
verified: false,
hobbies: [ 'reading', 'coding' ]
}
{
name: 'Stephen Prakash G',
age: 28,
verified: true,
hobbies: [ 'reading', 'coding' ]
}
*/
// Conclusion: Methods 1 and 2 create shallow clones, where nested objects or arrays are
// still referenced.
// Method 3 creates a deep clone, ensuring complete immutability.
// Method 1 (Spread Operator) and Method 2 (Object.assign()): Both create shallow clones.
//Modifying nested properties
// like hobbies in the clone affects the original object due to reference sharing.
// Method 3 (JSON.parse() and JSON.stringify()): Creates a deep clone. Modifying properties
// in this clone does not affect the original object because a new object and array are created.