JavaScript is a high-level, interpreted programming language used to create dynamic and interactive content on websites. It is part of the core technologies of the web alongside HTML and CSS. It supports object-oriented, imperative, and functional programming paradigms.
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Example</title>
</head>
<body>
<h1>Welcome!</h1>
<button onclick="sayHello()">Click Me</button>
<script>
function sayHello() {
alert('Hello, World!');
}
</script>
</body>
</html>
Here, JavaScript enables user interaction with a simple click event.
JavaScript has seven primitive data types:
"Hello"
).42
, 3.14
).true
or false
.Number.MAX_SAFE_INTEGER
.Non-primitive data types include:
let name = "John"; // String
let age = 30; // Number
let isStudent = false; // Boolean
let notAssigned; // Undefined
let emptyValue = null; // Null
let uniqueID = Symbol(); // Symbol
let largeNumber = 12345678901234567890n; // BigInt
let person = { // Object
name: "Alice",
age: 25
};
var
, let
, and const
?JavaScript provides three ways to declare variables: var
, let
, and const
. They differ in scope, re-declaration, and mutability.
Feature | var |
let |
const |
---|---|---|---|
Scope | Function-scoped | Block-scoped | Block-scoped |
Re-declaration | Allowed | Not allowed | Not allowed |
Initialization | Optional | Optional | Mandatory |
Value Reassignment | Allowed | Allowed | Not allowed |
// var example
var x = 10;
var x = 20; // Re-declaration allowed
console.log(x); // Output: 20
// let example
let y = 10;
// let y = 20; // Error: Cannot re-declare
y = 30; // Re-assignment allowed
console.log(y); // Output: 30
// const example
const z = 50;
// z = 60; // Error: Cannot re-assign
console.log(z); // Output: 50
Hoisting is JavaScript's behavior of moving declarations to the top of their scope during the compilation phase. However, only declarations are hoisted, not initializations.
Functions and var
declarations are hoisted, while let
and const
remain uninitialized in the temporal dead zone (TDZ).
// Example of Variable Hoisting
console.log(a); // Output: undefined (hoisted but uninitialized)
var a = 10;
// Example of Function Hoisting
greet(); // Output: Hello, World!
function greet() {
console.log("Hello, World!");
}
// let and const (No Initialization)
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 20;
A closure is a function that retains access to variables from its outer scope, even after the outer function has returned. Closures are fundamental for data privacy and encapsulation in JavaScript.
Closures allow functions to remember their lexical scope and create private variables.
function counter() {
let count = 0; // Outer variable
return function increment() {
count++; // Accesses the outer variable
return count;
};
}
const myCounter = counter();
console.log(myCounter()); // Output: 1
console.log(myCounter()); // Output: 2
==
and ===
?The ==
operator checks for equality after performing type coercion, meaning it converts the operands to the same type before comparison. On the other hand, the ===
operator (strict equality) checks for equality without performing type coercion, meaning both the value and the type must be the same.
console.log(5 == '5'); // true (type coercion)
console.log(5 === '5'); // false (strict comparison)
console.log(null == undefined); // true
console.log(null === undefined); // false
A Promise is an object representing the eventual completion or failure of an asynchronous operation. It allows you to handle asynchronous tasks in a cleaner way, avoiding callback hell.
Promises have three states:
const promise = new Promise((resolve, reject) => {
let success = true;
if (success) resolve("Operation Successful");
else reject("Operation Failed");
});
promise
.then(result => console.log(result)) // Output: Operation Successful
.catch(error => console.error(error));
The event loop is a mechanism that allows JavaScript to handle asynchronous operations. It ensures that tasks in the queue are executed after the current stack is cleared. The event loop constantly checks the call stack and the task queue to determine what to execute next.
console.log("Start");
setTimeout(() => {
console.log("Asynchronous Task");
}, 1000);
console.log("End");
// Output:
// Start
// End
// Asynchronous Task
An IIFE (Immediately Invoked Function Expression) is a function that is executed immediately after it is defined. It is commonly used to create a private scope and avoid polluting the global namespace.
(function() {
console.log("IIFE executed!");
})();
Here, the function is wrapped in parentheses to make it an expression, and it is immediately invoked by appending ()
.
slice()
and splice()
?Both slice()
and splice()
are used to work with arrays, but they serve different purposes:
slice()
: Returns a shallow copy of a portion of an array without modifying the original array.splice()
: Modifies the original array by adding or removing elements.
// slice() example
const arr = [1, 2, 3, 4, 5];
const sliced = arr.slice(1, 4); // [2, 3, 4]
console.log(arr); // [1, 2, 3, 4, 5]
// splice() example
const spliced = arr.splice(1, 2, 10, 20); // Removes 2, 3 and adds 10, 20
console.log(arr); // [1, 10, 20, 4, 5]
Arrow functions are a more concise way of writing functions in JavaScript. They do not have their own this
context, meaning they inherit this
from the enclosing scope. They are commonly used for shorter syntax and lexical scoping.
// Regular function
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => a + b;
console.log(add(2, 3)); // Output: 5
Template literals are string literals that allow embedded expressions. They are enclosed by backticks (`
) instead of quotes and can contain placeholders indicated by ${expression}
.
const name = "John";
const greeting = `Hello, ${name}!`;
console.log(greeting); // Output: Hello, John!
Destructuring is a syntax that allows unpacking values from arrays or properties from objects into distinct variables.
// Array destructuring
const [a, b] = [1, 2];
console.log(a, b); // Output: 1 2
// Object destructuring
const { name, age } = { name: "Alice", age: 25 };
console.log(name, age); // Output: Alice 25
call()
, apply()
, and bind()
?These methods are used to invoke functions with a specific this
value and arguments:
call()
: Invokes a function with arguments passed individually.apply()
: Invokes a function with arguments passed as an array.bind()
: Returns a new function with the specified this
value and arguments bound.
const obj = { name: "John" };
function greet(greeting) {
console.log(`${greeting}, ${this.name}`);
}
greet.call(obj, "Hello"); // Output: Hello, John
greet.apply(obj, ["Hi"]); // Output: Hi, John
const boundGreet = greet.bind(obj, "Hey");
boundGreet(); // Output: Hey, John
map()
and forEach()
?Both map()
and forEach()
iterate over arrays, but:
map()
: Creates a new array with the results of calling a provided function on every element in the array.forEach()
: Executes a provided function on each element but does not return a new array.
const arr = [1, 2, 3];
// map()
const doubled = arr.map(x => x * 2);
console.log(doubled); // [2, 4, 6]
// forEach()
arr.forEach(x => console.log(x)); // 1 2 3
An event is an action or occurrence detected by JavaScript, such as a user clicking a button, hovering over an element, or pressing a key. Events allow developers to define behavior for user interactions.
document.querySelector("button").addEventListener("click", () => {
console.log("Button clicked!");
});
Event bubbling is a mechanism where an event propagates from the innermost target element to its ancestors in the DOM tree. By default, events bubble up unless explicitly stopped.
document.querySelector("#child").addEventListener("click", () => {
console.log("Child clicked!");
});
document.querySelector("#parent").addEventListener("click", () => {
console.log("Parent clicked!");
});
// Clicking on the child element will trigger both listeners due to bubbling.
Event delegation is a technique where a single event listener is added to a parent element to manage events for its child elements. This leverages event bubbling and improves performance.
document.querySelector("#parent").addEventListener("click", (event) => {
if (event.target.tagName === "BUTTON") {
console.log("Button clicked:", event.target.textContent);
}
});
JavaScript modules allow code to be split into reusable pieces by using export
and import
. They promote modular development and better code organization.
// math.js
export const add = (a, b) => a + b;
// main.js
import { add } from "./math.js";
console.log(add(2, 3)); // Output: 5
setTimeout()
in JavaScript?setTimeout()
is used to execute a function after a specified delay (in milliseconds). It is commonly used for delayed executions or timers.
setTimeout(() => {
console.log("This message is displayed after 2 seconds");
}, 2000);
undefined
and null
?In JavaScript:
undefined
: Indicates that a variable has been declared but not yet assigned a value.null
: Represents the intentional absence of any object value.
let a; // undefined
console.log(a); // Output: undefined
let b = null; // null
console.log(b); // Output: null
JavaScript handles asynchronous operations using:
// Using Promises
fetch('https://api.example.com')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
A callback is a function passed as an argument to another function, which is executed after the completion of that function.
function greet(name, callback) {
console.log(`Hello, ${name}`);
callback();
}
greet("Alice", () => {
console.log("Callback executed.");
});
Synchronous JavaScript executes code sequentially, blocking further operations until the current one finishes. Asynchronous JavaScript allows operations to run in the background without blocking the execution thread.
// Synchronous
console.log("Start");
alert("Blocking operation");
console.log("End");
// Asynchronous
console.log("Start");
setTimeout(() => console.log("Delayed"), 1000);
console.log("End");
A higher-order function is a function that:
function higherOrderFunction(callback) {
console.log("Higher-order function executed.");
callback();
}
higherOrderFunction(() => {
console.log("Callback executed.");
});
Currying is a technique where a function is transformed into a sequence of functions, each taking a single argument.
function multiply(a) {
return function(b) {
return a * b;
};
}
const multiplyBy2 = multiply(2);
console.log(multiplyBy2(5)); // Output: 10
Prototypes are the mechanism by which JavaScript objects inherit properties and methods from other objects. Every JavaScript object has an internal property called [[Prototype]]
, which can be accessed via Object.getPrototypeOf()
.
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hello, ${this.name}`);
};
const alice = new Person("Alice");
alice.greet(); // Output: Hello, Alice
Object.freeze()
and Object.seal()
?
Object.freeze()
: Prevents any changes to an object, including adding, modifying, or deleting properties.
Object.seal()
: Allows modifying existing properties but prevents adding or deleting properties.
const obj = { name: "Alice" };
Object.freeze(obj);
obj.name = "Bob"; // No effect
const obj2 = { name: "Alice" };
Object.seal(obj2);
obj2.name = "Bob"; // Allowed
delete obj2.name; // Not allowed
A closure is a function that retains access to its outer scope, even after the outer function has finished execution. Closures are created every time a function is returned.
function outer() {
let count = 0;
return function inner() {
count++;
return count;
};
}
const counter = outer();
console.log(counter()); // Output: 1
console.log(counter()); // Output: 2
typeof
operator?The typeof
operator is used to determine the data type of a variable.
console.log(typeof "Hello"); // string
console.log(typeof 42); // number
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
console.log(typeof null); // object (historical bug)
A shallow copy creates a new object with references to the original object's properties, while a deep copy creates a new object with all nested properties copied recursively.
// Shallow Copy
const obj = { name: "Alice", details: { age: 25 } };
const shallowCopy = { ...obj };
shallowCopy.details.age = 30;
console.log(obj.details.age); // Output: 30
// Deep Copy
const deepCopy = JSON.parse(JSON.stringify(obj));
deepCopy.details.age = 40;
console.log(obj.details.age); // Output: 30
Default parameters allow you to initialize function parameters with default values if no value or undefined
is provided.
function greet(name = "Guest") {
console.log(`Hello, ${name}!`);
}
greet(); // Output: Hello, Guest!
greet("Alice"); // Output: Hello, Alice!
==
and ===
?The ==
operator performs type coercion before comparing, while ===
(strict equality) compares both value and type without coercion.
console.log(5 == "5"); // true (type coercion)
console.log(5 === "5"); // false (strict comparison)
NaN
in JavaScript?NaN
stands for "Not-a-Number" and represents a value that is not a legal number. It is the result of invalid or undefined mathematical operations.
console.log(0 / 0); // NaN
console.log(isNaN("text")); // true
strict mode
in JavaScript?'use strict'
is a directive introduced in ECMAScript 5 that enables stricter parsing and error handling in JavaScript. It prevents the use of undeclared variables and silent errors.
'use strict';
x = 10; // Error: x is not defined
Symbols are unique and immutable data types introduced in ES6. They are often used as object keys to create unique property names.
const sym1 = Symbol("id");
const sym2 = Symbol("id");
console.log(sym1 === sym2); // false
async/await
in JavaScript?async/await
is syntax introduced in ES2017 that allows writing asynchronous code in a synchronous-like manner. It is built on Promises.
async function fetchData() {
try {
const response = await fetch("https://api.example.com");
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Error:", error);
}
}
fetchData();
setInterval()
and setTimeout()
?Both are timing functions:
setInterval()
: Executes a function repeatedly at a specified interval.setTimeout()
: Executes a function once after a specified delay.
// setInterval example
setInterval(() => console.log("Repeats every 1 second"), 1000);
// setTimeout example
setTimeout(() => console.log("Executes after 3 seconds"), 3000);
Debouncing is a programming pattern to limit the execution of a function. The function executes after a specified time delay and resets if triggered again within the delay period.
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), delay);
};
}
const logMessage = debounce(() => console.log("Executed!"), 500);
logMessage();
Throttling ensures that a function is executed at most once in a specified time interval, even if triggered multiple times.
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function(...args) {
const context = this;
if (!lastRan) {
func.apply(context, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
if (Date.now() - lastRan >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
const logMessage = throttle(() => console.log("Throttled!"), 1000);
logMessage();
The DOM (Document Object Model) is a programming interface for HTML and XML documents. It represents the document as a tree of nodes, allowing JavaScript to manipulate, structure, and style the content dynamically.
// Accessing DOM elements
const heading = document.querySelector("h1");
heading.textContent = "New Heading!";
innerHTML
and textContent
?innerHTML
gets or sets the HTML content of an element, including tags, while textContent
retrieves or sets only the text content, ignoring any HTML tags.
const div = document.querySelector("div");
console.log(div.innerHTML); // Outputs HTML tags
console.log(div.textContent); // Outputs plain text
JavaScript events are actions or occurrences detected by JavaScript, such as user interactions (e.g., clicks, key presses) or browser events (e.g., page load, resize).
// Example: Handling a click event
document.querySelector("button").addEventListener("click", () => {
console.log("Button clicked!");
});
Promises are objects that represent the eventual completion (or failure) of an asynchronous operation. They allow chaining with .then()
and error handling with .catch()
.
const promise = new Promise((resolve, reject) => {
let success = true;
if (success) resolve("Operation successful");
else reject("Operation failed");
});
promise
.then(result => console.log(result))
.catch(error => console.error(error));
this
keyword in JavaScript?The this
keyword refers to the object that is currently executing the function. Its value depends on how the function is invoked:
window
in browsers).this
from the enclosing scope.
const obj = {
name: "Alice",
greet: function() {
console.log(this.name);
},
};
obj.greet(); // Output: Alice
A closure is a function that retains access to variables in its outer scope, even after the outer function has returned. Closures enable data encapsulation and private variables.
function outer() {
let count = 0;
return function inner() {
count++;
return count;
};
}
const counter = outer();
console.log(counter()); // Output: 1
console.log(counter()); // Output: 2
JSON
in JavaScript?JSON
(JavaScript Object Notation) is a lightweight data format used for exchanging data between servers and web applications. It is easy to read and write and is derived from JavaScript objects.
// Convert object to JSON string
const obj = { name: "Alice", age: 25 };
const jsonString = JSON.stringify(obj);
console.log(jsonString); // {"name":"Alice","age":25}
// Convert JSON string to object
const jsonObject = JSON.parse(jsonString);
console.log(jsonObject.name); // Alice
localStorage
and sessionStorage
?
localStorage
: Stores data with no expiration time and persists even after the browser is closed.
sessionStorage
: Stores data for the duration of the page session and is cleared when the tab is closed.
// localStorage example
localStorage.setItem("key", "value");
console.log(localStorage.getItem("key")); // value
// sessionStorage example
sessionStorage.setItem("key", "value");
console.log(sessionStorage.getItem("key")); // value
call()
, apply()
, and bind()
?
call()
: Invokes a function with arguments passed individually.
apply()
: Invokes a function with arguments passed as an array.
bind()
: Returns a new function with a specific this
context and arguments.
const obj = { name: "Alice" };
function greet(greeting) {
console.log(`${greeting}, ${this.name}`);
}
greet.call(obj, "Hello"); // Output: Hello, Alice
greet.apply(obj, ["Hi"]); // Output: Hi, Alice
const boundGreet = greet.bind(obj, "Hey");
boundGreet(); // Output: Hey, Alice
A shallow copy copies only the first level of an object, while a deep copy recursively copies all levels of an object.
// Shallow copy
const obj = { name: "Alice", details: { age: 25 } };
const shallowCopy = { ...obj };
shallowCopy.details.age = 30;
console.log(obj.details.age); // Output: 30
// Deep copy
const deepCopy = JSON.parse(JSON.stringify(obj));
deepCopy.details.age = 40;
console.log(obj.details.age); // Output: 30
Function Declaration: A named function defined using the function
keyword and hoisted to the top of its scope.
Function Expression: A function assigned to a variable or constant. It is not hoisted.
// Function Declaration
greet(); // Works due to hoisting
function greet() {
console.log("Hello!");
}
// Function Expression
const sayHi = function() {
console.log("Hi!");
};
sayHi(); // Works only after definition
JavaScript classes, introduced in ES6, are syntactic sugar over JavaScript's prototype-based inheritance. They provide a clearer and more structured way to create objects and define behavior.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old.`);
}
}
const john = new Person("John", 30);
john.greet(); // Output: Hi, I'm John and I'm 30 years old.
let
and const
?
let
: Used for variables that can be reassigned.
const
: Used for variables that cannot be reassigned. However, objects declared with const
can have their properties modified.
let x = 10;
x = 20; // Allowed
const y = 30;
// y = 40; // Error: Assignment to constant variable
const obj = { name: "Alice" };
obj.name = "Bob"; // Allowed
Iterators are objects that define a sequence and a way to access elements in that sequence. They implement the next()
method, which returns an object with value
and done
properties.
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
Generators are functions that can be paused and resumed using the yield
keyword. They are created using the function*
syntax and return an iterator.
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
}
const generator = generatorFunction();
console.log(generator.next().value); // 1
console.log(generator.next().value); // 2
console.log(generator.next().value); // 3
for...in
and for...of
?
for...in
: Iterates over the enumerable properties of an object.
for...of
: Iterates over the values of an iterable object (e.g., arrays, strings, maps).
const obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
console.log(key); // a, b, c
}
const arr = [10, 20, 30];
for (let value of arr) {
console.log(value); // 10, 20, 30
}
Promises represent the eventual result of an asynchronous operation. They can be in one of three states: pending, fulfilled, or rejected.
const promise = new Promise((resolve, reject) => {
let success = true;
if (success) resolve("Success");
else reject("Error");
});
promise
.then(result => console.log(result)) // Success
.catch(error => console.error(error)); // Error
apply()
and call()
?
call()
: Invokes a function with arguments passed individually.
apply()
: Invokes a function with arguments passed as an array.
function greet(greeting, name) {
console.log(`${greeting}, ${name}`);
}
greet.call(null, "Hello", "Alice"); // Hello, Alice
greet.apply(null, ["Hi", "Bob"]); // Hi, Bob
typeof
operator?The typeof
operator returns the data type of a variable or expression.
console.log(typeof "Hello"); // string
console.log(typeof 42); // number
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
console.log(typeof null); // object (historical bug)
Arrow functions are a shorter syntax for writing functions. They don't have their own this
context and are always anonymous.
// Regular function
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => a + b;
console.log(add(2, 3)); // 5
The Spread operator (...
) is used to expand iterable elements (like arrays or objects), while the Rest operator (...
) collects all remaining elements into an array.
// Spread example
const arr = [1, 2, 3];
const newArr = [...arr, 4, 5];
console.log(newArr); // [1, 2, 3, 4, 5]
// Rest example
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3)); // 6
WeakMap: A collection of key-value pairs where keys must be objects and are weakly referenced.
WeakSet: A collection of objects, also weakly referenced.
let obj = { name: "Alice" };
const weakMap = new WeakMap();
weakMap.set(obj, "Developer");
console.log(weakMap.get(obj)); // Developer
const weakSet = new WeakSet();
weakSet.add(obj);
console.log(weakSet.has(obj)); // true
A Proxy is an object that wraps another object and intercepts operations performed on it, such as reading or writing properties.
const target = { name: "Alice" };
const proxy = new Proxy(target, {
get: (obj, prop) => {
return prop in obj ? obj[prop] : "Property not found";
},
});
console.log(proxy.name); // Alice
console.log(proxy.age); // Property not found
Object.assign()
and the Spread operator?
Object.assign()
: Copies properties from one or more objects to a target object.
Spread Operator: Creates shallow copies of objects or arrays but with shorter syntax.
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
// Using Object.assign()
const result1 = Object.assign({}, obj1, obj2);
console.log(result1); // { a: 1, b: 3, c: 4 }
// Using Spread operator
const result2 = { ...obj1, ...obj2 };
console.log(result2); // { a: 1, b: 3, c: 4 }
The Promise.all()
method takes an array of Promises and resolves when all of them are resolved or rejects if any one of them is rejected.
const promise1 = Promise.resolve(10);
const promise2 = Promise.resolve(20);
Promise.all([promise1, promise2]).then(values => {
console.log(values); // [10, 20]
});
The Temporal Dead Zone refers to the period between when a variable is hoisted and when it is declared. Accessing the variable during this period results in a ReferenceError
.
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 10;
Promises: Provide a way to handle asynchronous operations using .then()
and .catch()
.
Async/Await: Simplifies working with Promises by writing asynchronous code that looks like synchronous code.
// Using Promises
fetch("https://api.example.com")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
// Using Async/Await
async function fetchData() {
try {
const response = await fetch("https://api.example.com");
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
fetchData();
Destructuring allows unpacking values from arrays or properties from objects into distinct variables.
const arr = [1, 2, 3];
const [a, b] = arr;
console.log(a, b); // 1, 2
const obj = { name: "Alice", age: 25 };
const { name, age } = obj;
console.log(name, age); // Alice, 25
fetch()
in JavaScript?fetch()
is used to make network requests and handle responses. It returns a Promise, making it ideal for asynchronous operations.
fetch("https://api.example.com")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error("Error:", error));
Memoization is an optimization technique that caches the results of expensive function calls and returns the cached result for the same input.
function memoize(fn) {
const cache = {};
return function (...args) {
const key = JSON.stringify(args);
if (!cache[key]) {
cache[key] = fn(...args);
}
return cache[key];
};
}
const add = (a, b) => a + b;
const memoizedAdd = memoize(add);
console.log(memoizedAdd(2, 3)); // 5 (calculated)
console.log(memoizedAdd(2, 3)); // 5 (cached)