A closure is a function that has access to its own scope, the scope of the outer function, and the global scope. It allows a function to "remember" the environment in which it was created, even after the outer function has finished execution.
Sample Code:
function outer() {
let outerVariable = 'I am from outer function';
function inner() {
console.log(outerVariable); // inner function can access outerVariable
}
return inner;
}
const closureExample = outer(); // outer() returns the inner function
closureExample(); // Logs: "I am from outer function"
var
: Function-scoped, can be re-declared and updated within its scope. It is hoisted but initialized with undefined
.let
: Block-scoped, can be updated but not re-declared within the same scope.const
: Block-scoped, cannot be updated or re-declared once initialized.Sample Code:
// var example
function varExample() {
var x = 10;
if (true) {
var x = 20; // Re-declared inside block, overwrites the previous value
console.log(x); // Logs: 20
}
console.log(x); // Logs: 20
}
varExample();
// let example
function letExample() {
let y = 10;
if (true) {
let y = 20; // Scoped to block
console.log(y); // Logs: 20
}
console.log(y); // Logs: 10
}
letExample();
// const example
function constExample() {
const z = 10;
// z = 20; // This will throw an error because const cannot be reassigned
console.log(z); // Logs: 10
}
constExample();
The value of this
in JavaScript depends on how a function is called. It can refer to the global object, the object that called the function, or be bound explicitly using methods like call
, apply
, or bind
.
Sample Code:
// In a regular function
function showThis() {
console.log(this);
}
showThis(); // In non-strict mode, it logs the global object (window in browsers)
// In an object method
const person = {
name: 'Alice',
greet: function() {
console.log(this.name);
}
};
person.greet(); // Logs: Alice
// Using `call` to change the context of `this`
function greetPerson() {
console.log(this.name);
}
const person2 = { name: 'Bob' };
greetPerson.call(person2); // Logs: Bob
Event delegation is a technique in JavaScript where instead of adding event listeners to each child element, a single event listener is added to the parent element. The parent element listens for events on its child elements, which is more efficient.
Sample Code:
// HTML structure
/*
<ul id="list">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
*/
document.getElementById('list').addEventListener('click', function(e) {
if (e.target && e.target.nodeName === 'LI') {
console.log(`You clicked on ${e.target.textContent}`);
}
});
null
: Represents the intentional absence of any value. It is an object in JavaScript.undefined
: Indicates that a variable has been declared but has not yet been assigned a value.Sample Code:
let a;
console.log(a); // Logs: undefined (a is declared but not assigned)
let b = null;
console.log(b); // Logs: null (null is explicitly assigned to b)
A Promise is an object representing the eventual completion (or failure) of an asynchronous operation and its resulting value. Promises can be in three states: pending, fulfilled, or rejected.
Sample Code:
function examplePromise() {
return new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve('Operation successful');
} else {
reject('Operation failed');
}
});
}
examplePromise()
.then((message) => {
console.log(message); // Logs: "Operation successful"
})
.catch((error) => {
console.log(error);
});
Arrow functions provide a shorter syntax for writing functions. They do not have their own this
value and inherit this
from the surrounding context.
Sample Code:
// Traditional function
const traditionalFunction = function() {
console.log(this);
};
// Arrow function
const arrowFunction = () => {
console.log(this); // Inherits `this` from the surrounding context
};
traditionalFunction(); // Logs: global object (or undefined in strict mode)
arrowFunction(); // Logs: inherited `this`
setTimeout
is a method that executes a function after a specified delay (in milliseconds).
Sample Code:
console.log('Start');
setTimeout(function() {
console.log('This runs after 2 seconds');
}, 2000); // 2000ms = 2 seconds
console.log('End');
// Output:
// Start
// End
// This runs after 2 seconds
==
(loose equality) compares values for equality after type coercion.===
(strict equality) compares both the values and the types without coercion.Sample Code:
console.log(5 == '5'); // Logs: true (due to type coercion)
console.log(5 === '5'); // Logs: false (different types)
let obj1 = {name: 'Alice'};
let obj2 = {name: 'Alice'};
console.log(obj1 == obj2); // Logs: false (different object references)
console.log(obj1 === obj2); // Logs: false (different object references)
bind()
is used to create a new function that, when called, has its this
value set to a specific object, with a given sequence of arguments.
Sample Code:
function greet() {
console.log(`Hello, ${this.name}`);
}
const person = { name: 'Charlie' };
const greetPerson = greet.bind(person);
greetPerson(); // Logs: Hello, Charlie
A higher-order function is a function that either takes one or more functions as arguments or returns a function as its result.
Sample Code:
// Function that accepts another function as an argument
function greetPerson(name, greetFunction) {
console.log(greetFunction(name));
}
function sayHello(name) {
return `Hello, ${name}!`;
}
greetPerson('Alice', sayHello); // Logs: Hello, Alice!
// Function that returns another function
function multiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = multiplier(2);
console.log(double(5)); // Logs: 10
setInterval()
is used to repeatedly execute a function at a specified interval in milliseconds.
Sample Code:
let count = 0;
const intervalId = setInterval(() => {
count++;
console.log(count); // Logs 1, 2, 3, ..., every second
if (count === 5) {
clearInterval(intervalId); // Stops the interval after 5 iterations
}
}, 1000);
bind()
: Creates a new function that, when called, has its this
keyword set to the provided value, with a given sequence of arguments.call()
: Immediately invokes the function and sets the this
value to the provided object, followed by the arguments.apply()
: Similar to call()
, but arguments are passed as an array.Sample Code:
function greet(message) {
console.log(`${message}, ${this.name}`);
}
const person = { name: 'Bob' };
const boundGreet = greet.bind(person);
boundGreet('Hello'); // Logs: Hello, Bob
greet.call(person, 'Hi'); // Logs: Hi, Bob
greet.apply(person, ['Good morning']); // Logs: Good morning, Bob
async
and await
provide a more readable and straightforward way to work with asynchronous code. The async
keyword is used to declare a function that returns a promise, and await
is used to wait for a promise to resolve or reject inside an async
function.
Sample Code:
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => resolve('Data fetched!'), 2000);
});
}
async function getData() {
console.log('Fetching...');
const result = await fetchData(); // Waits for fetchData to resolve
console.log(result); // Logs: Data fetched!
}
getData();
slice()
: Returns a shallow copy of a portion of an array or string without modifying the original.splice()
: Changes the contents of an array by removing or replacing existing elements and/or adding new elements in place.Sample Code:
// slice example
const array1 = [1, 2, 3, 4, 5];
const slicedArray = array1.slice(1, 3); // Returns a new array [2, 3]
console.log(slicedArray); // Logs: [2, 3]
// splice example
const array2 = [1, 2, 3, 4, 5];
array2.splice(2, 2, 'a', 'b'); // Removes 2 elements starting from index 2 and adds 'a' and 'b'
console.log(array2); // Logs: [1, 2, 'a', 'b', 5]
Object.freeze()
: Makes an object immutable. Properties cannot be added, removed, or modified.Object.seal()
: Prevents properties from being added or removed, but existing properties can still be modified.Sample Code:
const obj = { name: 'John', age: 30 };
// Using Object.freeze
Object.freeze(obj);
obj.age = 31; // Won't work because the object is frozen
console.log(obj.age); // Logs: 30
// Using Object.seal
const obj2 = { name: 'Alice', age: 25 };
Object.seal(obj2);
obj2.age = 26; // This works because the properties can be modified
obj2.city = 'NY'; // Won't work because new properties can't be added
console.log(obj2); // Logs: { name: 'Alice', age: 26 }
Destructuring allows unpacking values from arrays or objects into distinct variables in a concise syntax.
Sample Code:
// Array destructuring
const numbers = [10, 20, 30];
const [a, b, c] = numbers;
console.log(a, b, c); // Logs: 10 20 30
// Object destructuring
const person = { name: 'Charlie', age: 35 };
const { name, age } = person;
console.log(name, age); // Logs: Charlie 35
// Nested destructuring
const user = { profile: { name: 'Dave', city: 'Berlin' } };
const { profile: { name: userName, city } } = user;
console.log(userName, city); // Logs: Dave Berlin
A module is a way to organize JavaScript code by breaking it into separate files, allowing specific functions or variables to be imported and exported between files. Modules help in better code maintainability.
Sample Code (using ES6 syntax):
// math.js (module)
export function add(a, b) {
return a + b;
}
// app.js
import { add } from './math.js';
console.log(add(2, 3)); // Logs: 5
The event loop is the mechanism in JavaScript that handles asynchronous code execution. It continuously checks the call stack and the task queue to process tasks (like function calls or events). If the stack is empty, it moves tasks from the task queue to the call stack.
Sample Code:
console.log('Start');
setTimeout(() => {
console.log('Middle');
}, 0);
console.log('End');
// Output:
// Start
// End
// Middle
localStorage
: Stores data with no expiration time. Data remains even after the browser is closed.sessionStorage
: Stores data for the duration of the page session. Data is cleared when the page session ends (i.e., when the browser or tab is closed).Sample Code:
// Using localStorage
localStorage.setItem('name', 'Alice');
console.log(localStorage.getItem('name')); // Logs: Alice
// Using sessionStorage
sessionStorage.setItem('sessionName', 'Bob');
console.log(sessionStorage.getItem('sessionName')); // Logs: Bob
In arrow functions, this
does not refer to the object that calls the function, but instead, it inherits the this
value from the surrounding context (lexical scoping).
Sample Code:
const person = {
name: 'Alice',
greet: function() {
setTimeout(() => {
console.log(`Hello, ${this.name}`); // Arrow function uses lexical scoping
}, 1000);
}
};
person.greet(); // Logs: Hello, Alice
If we had used a regular function inside setTimeout
, this
would refer to the global object, and the name property would be undefined.
map()
: Returns a new array with the results of calling a provided function on every element of the array.forEach()
: Executes a provided function once for each element in the array but does not return anything (undefined).Sample Code:
// map example
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // Logs: [2, 4, 6]
// forEach example
const numbers2 = [1, 2, 3];
numbers2.forEach(num => console.log(num * 2));
// Logs: 2
// Logs: 4
// Logs: 6
setTimeout()
: Executes a function after a specified delay (in milliseconds).setImmediate()
: Executes a function immediately after the current event loop cycle (next iteration).Sample Code:
setTimeout(() => {
console.log('setTimeout');
}, 0);
setImmediate(() => {
console.log('setImmediate');
});
// Logs:
// setImmediate
// setTimeout
Generators are special functions that can be paused and resumed, allowing for more control over function execution. They are defined using function*
syntax and use the yield
keyword to pause and return a value.
Sample Code:
function* generateNumbers() {
yield 1;
yield 2;
yield 3;
}
const generator = generateNumbers();
console.log(generator.next().value); // Logs: 1
console.log(generator.next().value); // Logs: 2
console.log(generator.next().value); // Logs: 3
console.log(generator.next().value); // Logs: undefined
A Proxy
object enables the creation of custom behavior for fundamental operations (such as property lookup, assignment, etc.) on an object. You can define traps (handlers) to intercept and redefine operations like get
, set
, etc.
Sample Code:
const person = {
name: 'Alice',
age: 30
};
const handler = {
get(target, prop) {
if (prop in target) {
return `Property ${prop} is ${target[prop]}`;
}
return `Property ${prop} does not exist`;
}
};
const proxyPerson = new Proxy(person, handler);
console.log(proxyPerson.name); // Logs: Property name is Alice
console.log(proxyPerson.age); // Logs: Property age is 30
console.log(proxyPerson.city); // Logs: Property city does not exist
Set
: A collection of unique values, where no duplicate values are allowed.Map
: A collection of key-value pairs where keys can be of any data type, and each key is unique.Sample Code:
// Set example
const uniqueNumbers = new Set([1, 2, 3, 3, 4, 5, 5]);
console.log(uniqueNumbers); // Logs: Set { 1, 2, 3, 4, 5 }
// Map example
const map = new Map();
map.set('name', 'Alice');
map.set('age', 30);
console.log(map.get('name')); // Logs: Alice
console.log(map.get('age')); // Logs: 30
Promises chaining allows multiple then()
or catch()
methods to be linked together to perform sequential asynchronous operations. Each then()
method returns a new promise, allowing further chaining.
Sample Code:
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => resolve('Data fetched!'), 1000);
});
}
fetchData()
.then((result) => {
console.log(result); // Logs: Data fetched!
return 'Processing data...';
})
.then((message) => {
console.log(message); // Logs: Processing data...
return 'Finished processing';
})
.then((message) => {
console.log(message); // Logs: Finished processing
})
.catch((error) => {
console.log('Error:', error);
});
WeakMap
: A collection of key-value pairs where the keys must be objects, and the values can be any data type. The keys are weakly referenced, meaning they are garbage-collected if there are no other references to them.WeakSet
: A collection of unique objects where the objects are weakly referenced and can be garbage-collected when there are no other references to them.Sample Code:
// WeakMap example
let obj1 = {};
let weakMap = new WeakMap();
weakMap.set(obj1, 'value');
console.log(weakMap.get(obj1)); // Logs: value
obj1 = null; // obj1 is now eligible for garbage collection
// WeakSet example
let obj2 = {};
let weakSet = new WeakSet();
weakSet.add(obj2);
console.log(weakSet.has(obj2)); // Logs: true
obj2 = null; // obj2 is eligible for garbage collection
The spread operator (...
) allows you to unpack elements from an array or object. It is useful for copying or merging data.
Sample Code:
// Spread in arrays
const numbers = [1, 2, 3];
const moreNumbers = [...numbers, 4, 5];
console.log(moreNumbers); // Logs: [1, 2, 3, 4, 5]
// Spread in objects
const person = { name: 'Alice', age: 30 };
const newPerson = { ...person, city: 'New York' };
console.log(newPerson); // Logs: { name: 'Alice', age: 30, city: 'New York' }
slice()
: Returns a shallow copy of a portion of an array (or string) without modifying the original array.splice()
: Changes the contents of an array by removing, replacing, or adding elements in place.Sample Code:
// slice example
const arr1 = [1, 2, 3, 4, 5];
const slicedArr = arr1.slice(1, 3); // Returns [2, 3]
console.log(slicedArr); // Logs: [2, 3]
// splice example
const arr2 = [1, 2, 3, 4, 5];
arr2.splice(2, 2, 'a', 'b'); // Removes 2 elements starting at index 2 and adds 'a' and 'b'
console.log(arr2); // Logs: [1, 2, 'a', 'b', 5]
A Set
is a collection of unique values. It can store any type of data, and it automatically removes duplicate values.
Sample Code:
const mySet = new Set();
mySet.add(1);
mySet.add(2);
mySet.add(3);
mySet.add(2); // Duplicate value, won't be added
console.log(mySet); // Logs: Set { 1, 2, 3 }
arguments
: An array-like object that contains all the arguments passed to a function (except in arrow functions).rest parameters
: A syntax that allows us to represent an indefinite number of arguments as an array. It is used in the function definition.Sample Code:
// Using arguments (works with traditional functions only)
function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(sum(1, 2, 3)); // Logs: 6
// Using rest parameters
function sumRest(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sumRest(1, 2, 3)); // Logs: 6
eval()
is a function that evaluates a string of JavaScript code and executes it. However, it can pose serious security risks (like code injection attacks) and should generally be avoided.
Sample Code:
const result = eval("2 + 2");
console.log(result); // Logs: 4
// Potential danger example
const userInput = "alert('You have been hacked!')";
eval(userInput); // This could execute malicious code if the input is controlled by the user.
process.nextTick()
: Runs a callback after the current operation completes and before any I/O tasks. It's part of Node.js and used for high-priority callbacks.setImmediate()
: Executes the callback after the current event loop cycle, but after any I/O tasks.Sample Code:
console.log('Start');
// process.nextTick example
process.nextTick(() => {
console.log('nextTick callback');
});
// setImmediate example
setImmediate(() => {
console.log('setImmediate callback');
});
console.log('End');
// Logs:
// Start
// End
// nextTick callback
// setImmediate callback
Sample Code:
// Synchronous function
function syncFunction() {
console.log('Start');
console.log('End');
}
syncFunction();
// Logs:
// Start
// End
// Asynchronous function
function asyncFunction() {
console.log('Start');
setTimeout(() => {
console.log('Inside setTimeout');
}, 1000);
console.log('End');
}
asyncFunction();
// Logs:
// Start
// End
// Inside setTimeout
A Symbol
is a primitive data type introduced in ES6 that is used to create unique identifiers for object properties. Each Symbol
is unique, ensuring that no property name conflicts occur.
Sample Code:
const mySymbol = Symbol('description');
const obj = {
[mySymbol]: 'value'
};
console.log(obj[mySymbol]); // Logs: value
console.log(mySymbol); // Logs: Symbol(description)
By default, JavaScript uses event bubbling, but we can specify event capturing by passing true
to addEventListener()
.
Sample Code:
document.getElementById('child').addEventListener('click', () => {
console.log('Child clicked');
}, false); // Bubbling phase
document.getElementById('parent').addEventListener('click', () => {
console.log('Parent clicked');
}, true); // Capturing phase
The reduce()
method executes a reducer function (that you provide) on each element of the array (from left to right) and reduces the array to a single value.
Sample Code:
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // Logs: 10
const product = numbers.reduce((accumulator, currentValue) => accumulator * currentValue, 1);
console.log(product); // Logs: 24
Destructuring allows you to extract values from arrays or objects and assign them to variables in a concise syntax.
Sample Code:
// Array Destructuring
const arr = [10, 20, 30];
const [a, b] = arr;
console.log(a, b); // Logs: 10 20
// Object Destructuring
const person = { name: 'John', age: 25 };
const { name, age } = person;
console.log(name, age); // Logs: John 25
The new
keyword is used to create a new instance of an object that has a constructor function. It sets the context of this
inside the constructor function to the newly created object.
Sample Code:
function Person(name, age) {
this.name = name;
this.age = age;
}
const person1 = new Person('Alice', 30);
console.log(person1); // Logs: Person { name: 'Alice', age: 30 }
call()
: Immediately invokes the function with a given this
context and arguments passed individually.apply()
: Similar to call()
, but the arguments are passed as an array.bind()
: Returns a new function with the this
context set to the specified value, and optional arguments.Sample Code:
function greet(city) {
console.log(`${this.name} lives in ${city}`);
}
const person = { name: 'Alice' };
greet.call(person, 'Paris'); // Logs: Alice lives in Paris
greet.apply(person, ['London']); // Logs: Alice lives in London
const boundGreet = greet.bind(person);
boundGreet('New York'); // Logs: Alice lives in New York
function longestUniqueSubstring(str) {
let maxLength = 0;
let start = 0;
const charMap = {};
for (let end = 0; end < str.length; end++) {
const char = str[end];
if (charMap[char] !== undefined && charMap[char] >= start) {
start = charMap[char] + 1;
}
charMap[char] = end;
maxLength = Math.max(maxLength, end - start + 1);
}
return maxLength;
}
// Example usage:
console.log(longestUniqueSubstring('abcabcbb')); // Output: 3 (abc)
start
and end
) to track the substring. We store the last index of each character in charMap
. When a duplicate character is found, we update the start
pointer to the position after the last occurrence of the character.