Arrays , Objects and Functions
Arrays in JavaScript
Section titled “Arrays in JavaScript”Arrays in JavaScript are like ordered lists that:
- Start counting from 0 (e.g., first item is at position 0)
- Can grow or shrink in size
- Can store any type of data (numbers, strings, objects, even other arrays)
- Can be modified after creation
// Basic array creation and usageconst numbers = [1, 2, 3, 4, 5];const mixed = ['text', 42, true, { key: 'value' }, [1, 2]];
Array Copy Behaviors
Section titled “Array Copy Behaviors”1. Reference Copy
Section titled “1. Reference Copy”The simplest form of copying - creates a new reference to the same array.
const original = [1, 2, {x: 3}];const reference = original; // Same array in memory
reference.push(4);console.log(original); // [1, 2, {x: 3}, 4] (affected)console.log(reference); // [1, 2, {x: 3}, 4] (affected)
2. Shallow Copy
Section titled “2. Shallow Copy”Creates a new array but maintains references to nested objects.
Shallow Copy Visualization ═══════════════════════════════════
Original Array Copied Array┌──────────────┐ ┌──────────────┐│ arr1 │ │ arr2 │├──────────────┤ ├──────────────┤│ [0]: 1 │ │ [0]: 1 ││ [1]: 2 │ │ [1]: 2 ││ [2]: ─────┐ │ │ [2]: ─────┐ │└───────────┼──┘ └───────────┼──┘ │ │ │ Heap Memory │ │ ┌────────────┐ │ └─→│ {x: 10} │←────┘ └────────────┘
// Shallow copy methodsconst original = [1, 2, {x: 3}];const shallowCopy1 = [...original]; // Spread operatorconst shallowCopy2 = Array.from(original); // Array.from()const shallowCopy3 = original.slice(); // slice()
// Demonstrating shared referencesshallowCopy1[2].x = 100;console.log(original[2].x); // 100 (nested object affected)
3. Deep Copy
Section titled “3. Deep Copy”Creates a completely independent copy with new references for all nested items.
Deep Copy Visualization ═══════════════════════════════════
Original Array Deep Copied Array┌──────────────┐ ┌──────────────┐│ arr1 │ │ arr2 │├──────────────┤ ├──────────────┤│ [0]: 1 │ │ [0]: 1 ││ [1]: 2 │ │ [1]: 2 ││ [2]: ─────┐ │ │ [2]: ─────┐ │└───────────┼──┘ └───────────┼──┘ │ │ │ Heap Memory │ │ ┌────────────┐ │ ┌────────────┐ └─→│ {x: 10} │ └─→│ {x: 10} │ └────────────┘ └────────────┘ (Object #1) (Object #2)
// Modern deep copy methodsconst original = [1, 2, {x: 3}];
// Method 1: structuredClone (recommended)const deepCopy1 = structuredClone(original);
// Method 2: JSON parse/stringify (with limitations)const deepCopy2 = JSON.parse(JSON.stringify(original));
// Demonstrating independencedeepCopy1[2].x = 200;console.log(original[2].x); // Still 3 (unaffected)
Example
Section titled “Example”const users = [ { id: 1, name: 'Alice', settings: { theme: 'dark' } }, { id: 2, name: 'Bob', settings: { theme: 'light' } }];
// Shallow copy behaviorconst shallowUsers = [...users];shallowUsers[0].settings.theme = 'light';console.log(users[0].settings.theme); // 'light' (affected)
// Deep copy behaviorconst deepUsers = structuredClone(users);deepUsers[0].settings.theme = 'dark';console.log(users[0].settings.theme); // 'light' (unaffected)
Array Methods
Section titled “Array Methods”Common array methods can be categorized by whether they modify the original array:
Non-mutating Methods (Return new array)
Section titled “Non-mutating Methods (Return new array)”const arr = [1, 2, 3, 4, 5];arr.map(item => item * 2); // Returns [2, 4, 6, 8, 10], arr unchangedarr.filter(item => item % 2 === 0); // Returns [2, 4], arr unchangedarr.reduce((acc, item) => acc + item, 0); // Returns 15, arr unchangedarr.slice(1, 4); // Returns [2, 3, 4], arr unchangedarr.forEach(item => console.log(item)); // Returns undefined, arr unchangedarr.join('-'); // Returns '1-2-3-4-5', arr unchanged
Mutating Methods (Modify original array)
Section titled “Mutating Methods (Modify original array)”const arr = [1, 2, 3, 4, 5];arr.splice(1, 2); // Returns [2, 3], arr is now [1, 4, 5]arr.push(6); // Returns new length 4, arr is now [1, 4, 5, 6]arr.pop(); // Returns 6, arr is now [1, 4, 5]arr.forEach(item => { // Returns undefined, can mutate arr if callback modifies it console.log(item);});arr.sort((a, b) => a - b); // Returns sorted array, arr is now [1, 4, 5, 6].// Note: sort() method modifies the original array.
Chaining Methods
Section titled “Chaining Methods”Higher-order methods can be chained together for complex operations.
const products = [ { name: 'Laptop', price: 1000, inStock: true }, { name: 'Phone', price: 500, inStock: false }, { name: 'Tablet', price: 300, inStock: true }];
const totalInStock = products .filter(product => product.inStock) .map(product => product.price) .reduce((acc, price) => acc + price, 0);
console.log(totalInStock); // 1300
Array Loops
Section titled “Array Loops”- Array loops are used to iterate over the elements of an array.
const numbers = [1, 2, 3, 4, 5];for (let i = 0; i < numbers.length; i++) { console.log(numbers[i]);}
forEach()
Section titled “forEach()”The forEach()
method executes a provided function once for each array element.
// Basic syntaxarray.forEach(function(element, index, array) { // Your code here});
// Simple exampleconst numbers = [1, 2, 3];numbers.forEach(num => console.log(num)); // Logs: 1, 2, 3
// With indexnumbers.forEach((num, index) => { console.log(`Index ${index}: ${num}`);}); // Logs: "Index 0: 1", "Index 1: 2", "Index 2: 3"
// With arraynumbers.forEach((num, index, array) => { console.log(array);}); // Logs: [1, 2, 3]
Key Features:
- Always returns
undefined
- Cannot break or return from the loop
- Skips empty array elements
- Modifies the original array if the callback function does so
Common Use Cases:
// 1. Modifying objects in an arrayconst users = [ { name: 'Alice', active: false }, { name: 'Bob', active: false }];users.forEach(user => user.active = true);
// 2. Side effects (like DOM updates)const items = ['apple', 'banana', 'orange'];items.forEach(item => { const li = document.createElement('li'); li.textContent = item; list.appendChild(li);});
// 3. Accumulating valueslet sum = 0;[1, 2, 3].forEach(num => sum += num);
Objects
Section titled “Objects”- Objects are collections of key-value pairs
- Keys are strings (or symbols), values can be any type of data
- Objects are mutable
- Objects are unordered
const user = { name: 'Alice', age: 25, isAdmin: true, "color": red};
console.log(user.name); // Aliceconsole.log(user['age']); // 25console.log(user["color"]); // red
Object Methods
Section titled “Object Methods”const obj1 = { name: "Alice", age: 25};
const obj2 = { name1: "Bob", age1: 30};
const obj3 = {...obj1, ...obj2}; // Spread operator
console.log(obj3.name); // Aliceconsole.log(obj3.age1); // 30console.log(obj3); // { name: 'Alice', age: 25, name1: 'Bob', age1: 30 }
- To learn more about the object methods, visit MDN.
Map and Set Collections
Section titled “Map and Set Collections”- A collection that lets you store key-value pairs where keys can be of any type (objects, functions, primitives).
- Map is not iterable for in loop.
// Creating and using Mapsconst userRoles = new Map();userRoles.set('john', 'admin'); // Add entryuserRoles.get('john'); // Get value: 'admin'userRoles.has('john'); // Check existence: trueuserRoles.delete('john'); // Remove entryuserRoles.size; // Get size
// Iteratingfor (const [user, role] of userRoles) { console.log(`${user} is ${role}`);}
Common Use Cases:
- Caching/memoization results
// Example: Simple cacheconst cache = new Map();function expensiveOperation(input) { if (cache.has(input)) return cache.get(input); const result = /* complex calculation */; cache.set(input, result); return result;}
- A collection of unique values where duplicates are automatically removed.
// Creating and using Setsconst uniqueTags = new Set(['js', 'python', 'js']); // Duplicates removeduniqueTags.add('java'); // Add valueuniqueTags.has('python'); // Check existence: trueuniqueTags.delete('js'); // Remove valueuniqueTags.size; // Get size
// Quick array deduplicationconst array = [1, 2, 2, 3, 3];const unique = [...new Set(array)]; // [1, 2, 3]
Common Use Cases:
- Removing duplicates from arrays
- Tracking unique visitors/events
- Managing selected items in UI
// Example: Tracking unique usersconst activeUsers = new Set();function trackUserActivity(userId) { activeUsers.add(userId); console.log(`Active users: ${activeUsers.size}`);}
Destructuring
Section titled “Destructuring”Destructuring allows you to unpack values from arrays or properties from objects into distinct variables. This syntax makes it easier to work with complex data structures.
Object Destructuring
Section titled “Object Destructuring”// Basic destructuringconst person = { name: 'Alice', age: 25, city: 'London' };const { name, age } = person;console.log(name); // 'Alice'console.log(age); // 25
// Destructuring with different variable namesconst { name: firstName, age: yearsOld } = person;console.log(firstName); // 'Alice'console.log(yearsOld); // 25
// Default valuesconst { country = 'Unknown' } = person;console.log(country); // 'Unknown' (since it wasn't in person object)
Array Destructuring
Section titled “Array Destructuring”// Basic array destructuringconst colors = ['red', 'green', 'blue'];const [first, second] = colors;console.log(first); // 'red'console.log(second); // 'green'
// Skip elements using commasconst [,, third] = colors;console.log(third); // 'blue'
// Rest patternconst [primary, ...rest] = colors;console.log(primary); // 'red'console.log(rest); // ['green', 'blue']
Nested Destructuring
Section titled “Nested Destructuring”const data = { user: { id: 1, details: { firstName: 'Alice', lastName: 'Smith' } }, scores: [10, 20, 30]};
// Destructuring nested objects and arraysconst { user: { details: { firstName, lastName }, id }, scores: [firstScore, ...otherScores]} = data;
console.log(firstName); // 'Alice'console.log(id); // 1console.log(firstScore); // 10console.log(otherScores); // [20, 30]
// JSON is a string representation of a JavaScript object.
{ "name": "Alice", "age": 25, "data1":{ "name1": "Bob", "age1": 30, "data2":{ "name2": "Charlie", "age2": 35, "data3":{ "name3": "David", "age3": 40 } } }}
// JSON.parse() is used to parse a JSON string and convert it into a JavaScript object.// JSON.stringify() is used to convert a JavaScript object into a JSON string.
Functions
Section titled “Functions”- Functions are blocks of code that perform a specific task.
- Functions can take input parameters and return output values.
- Functions can be used to organize code and make it more modular and reusable.
function add(a, b) { return a + b;}
console.log(add(1, 2)); // 3
Functions with Objects and Arrays
Section titled “Functions with Objects and Arrays”Functions can work with complex data types like objects and arrays. Here are some common patterns:
// Object as parameterfunction processUser(user) { return `${user.firstName} ${user.lastName} is ${user.age} years old`;}
const user = { firstName: 'John', lastName: 'Doe', age: 30};console.log(processUser(user)); // "John Doe is 30 years old"
// Array as parameterfunction calculateAverage(numbers) { const sum = numbers.reduce((acc, curr) => acc + curr, 0); return sum / numbers.length;}
const scores = [85, 92, 78, 95, 88];console.log(calculateAverage(scores)); // 87.6
// Returning an objectfunction createPerson(name, age) { return { name, age, greet() { return `Hello, I'm ${this.name}`; } };}
// Returning an arrayfunction splitAndSort(text) { return text.split(' ').sort();}
console.log(splitAndSort('apple banana cherry')); // ['apple', 'banana', 'cherry']
Variable Scope
Section titled “Variable Scope”JavaScript has several types of scope:
- Global Scope: Variables accessible throughout the entire program
- Function Scope: Variables only accessible within their containing function
- Block Scope: Variables (using
let
/const
) only accessible within their containing block
// Global scopeconst globalVar = 'I am global';
function exampleFunction() { // Function scope const functionVar = 'I am function-scoped';
if (true) { // Block scope const blockVar = 'I am block-scoped'; console.log(blockVar); // Works console.log(functionVar); // Works console.log(globalVar); // Works }
console.log(functionVar); // Works console.log(blockVar); // ReferenceError: blockVar is not defined}
console.log(globalVar); // Worksconsole.log(functionVar); // ReferenceError: functionVar is not defined
Hoisting: A JavaScript Legacy Challenge
Section titled “Hoisting: A JavaScript Legacy Challenge”Hoisting is a JavaScript behavior where variable and function declarations are moved to the top of their scope during code execution. While it’s considered a bad practice in modern JavaScript, understanding it is crucial for maintaining legacy code.
// What developers writeconsole.log(myVar); // undefined (doesn't crash!)var myVar = "Hello";
// What JavaScript actually seesvar myVar; // Declaration is "hoisted" upconsole.log(myVar); // undefinedmyVar = "Hello"; // Assignment stays here
Why Hoisting is Problematic
Section titled “Why Hoisting is Problematic”- Confusing Code Flow:
// Bad practice (but works due to hoisting)calculateTotal(100); // Works! But why?
function calculateTotal(amount) { console.log(amount * tax); // undefined × 100 = NaN var tax = 0.2; // Declaration hoisted, but not the value}
// Better practiceconst tax = 0.2;function calculateTotal(amount) { console.log(amount * tax); // Clear and predictable}calculateTotal(100);
- Function Declaration vs Expression:
// Function declarations are fully hoisteddoSomething(); // Works!function doSomething() { console.log("I'm hoisted!");}
// Function expressions are notdoSomethingElse(); // Error!const doSomethingElse = () => { console.log("I'm not hoisted!");};
Legacy Code Examples
Section titled “Legacy Code Examples”// Common in legacy code - relying on hoistingfunction oldCodebase() { for(var i = 0; i < items.length; i++) { // var is hoisted to function scope } console.log(i); // Still accessible! 😱
if(true) { var secret = "123"; // Hoisted to function scope } console.log(secret); // Accessible outside if block! 😱}
// Modern approachfunction modernCode() { for(let i = 0; i < items.length; i++) { // let has block scope } console.log(i); // ReferenceError ✅
if(true) { const secret = "123"; // Block scoped } console.log(secret); // ReferenceError ✅}
Arrow Functions vs Regular Functions
Section titled “Arrow Functions vs Regular Functions”Arrow functions and regular functions have several key differences in syntax and behavior:
// Regular Functionfunction regularFunction(a, b) { return a + b;}
// Arrow Functionconst arrowFunction = (a, b) => a + b;
Key Differences
Section titled “Key Differences”this
Binding:
const obj = { name: 'Example', // Regular function regularMethod: function() { setTimeout(function() { console.log(this.name); // undefined }, 100); }, // Arrow function arrowMethod: function() { setTimeout(() => { console.log(this.name); // 'Example' }, 100); }};
obj.regularMethod(); // undefinedobj.arrowMethod(); // Example
- Constructor Usage:
// Regular functions can be constructorsfunction Car(make) { this.make = make;}const myCar = new Car('Toyota'); // Works
// Arrow functions cannot be constructorsconst CarArrow = (make) => { this.make = make;};const myCarArrow = new CarArrow('Toyota'); // TypeError
- Arguments Object:
// Regular function has arguments objectfunction regular() { console.log(arguments); // [1, 2, 3]}
// Arrow function doesn't have argumentsconst arrow = () => { console.log(arguments); // ReferenceError};
regular(1, 2, 3); // [1, 2, 3]arrow(1, 2, 3); // ReferenceError
- Implicit Return:
// Regular function needs explicit returnfunction addRegular(a, b) { return a + b;}
// Arrow function with implicit returnconst addArrow = (a, b) => a + b;
// Arrow function with object implicit returnconst createObj = (name) => ({ name });
IIFE (Immediately Invoked Function Expression)
Section titled “IIFE (Immediately Invoked Function Expression)”- IIFE is a function that is defined and executed immediately.
- It is used to create a new execution context and avoid polluting the global scope.
(function() { console.log('IIFE executed!');})(); // IIFE executed!// Parentheses are required to invoke the IIFE.// Semicolons are required after IIFE to separate it from the next statement.
// Arrow functions can also be used as IIFE(() => { console.log('Arrow IIFE executed!');})(); // Arrow IIFE executed!