Advanced JavaScript Guide
Welcome to the Advanced JavaScript Guide! This guide is designed for developers who have a basic understanding of JavaScript and want to delve deeper into advanced concepts. We’ll cover complex topics, provide code samples, and include quizzes and exercises to test your knowledge.
Advanced Functions
Functions are the building blocks of JavaScript. In this section, we’ll explore advanced concepts like closures, currying, and higher-order functions.
Closures
A closure is a function that has access to its own scope, the outer function’s scope, and the global scope.
Example:
function outerFunction() {
let outerVariable = ‘I am outside!’;
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
const closureFunc = outerFunction();
closureFunc(); // Outputs: I am outside!
Explanation:
- innerFunction forms a closure with outerFunction, allowing it to access outerVariable even after outerFunction has completed execution.
Quiz Question
Q1: What will be the output of the following code?
function makeCounter() {
let count = 0;
return function () {
count += 1;
return count;
};
}
const counter1 = makeCounter();
console.log(counter1());
console.log(counter1());
A. 1, 1
B. 1, 2
C. 2, 3
D. Undefined, Undefined
Answer: B. 1, 2
Currying
Currying is the process of transforming a function with multiple arguments into a sequence of nested functions that take one argument at a time.
Example:
function multiply(a) {
return function (b) {
return a * b;
};
}
const double = multiply(2);
console.log(double(5)); // Outputs: 10
Exercise
E1: Create a curried function add that adds three numbers.
Solution:
function add(a) {
return function (b) {
return function (c) {
return a + b + c;
};
};
}
console.log(add(1)(2)(3)); // Outputs: 6
Higher-Order Functions
A higher-order function is a function that takes another function as an argument or returns a function as a result.
Example:
function greet(name) {
return function (message) {
console.log(`${message}, ${name}!`);
};
}
const greetJohn = greet(‘John’);
greetJohn(‘Hello’); // Outputs: Hello, John!
Quiz Question
Q2: Which of the following is not a higher-order function?
A. A function that returns another function
B. A function that accepts a function as an argument
C. A function that does not interact with other functions
D. A function that manipulates functions and returns a value
Answer: C. A function that does not interact with other functions
Asynchronous JavaScript
JavaScript is single-threaded but can handle asynchronous operations through callbacks, Promises, and async/await.
Promises
A Promise represents a value that may be available now, later, or never.
Example:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(‘Data fetched’);
}, 2000);
});
promise.then((data) => console.log(data)); // Outputs: Data fetched (after 2 seconds)
Exercise
E2: Create a Promise that rejects with an error message after 1 second.
Solution:
const errorPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(‘An error occurred’);
}, 1000);
});
errorPromise.catch((error) => console.error(error)); // Outputs: An error occurred
Async/Await
async and await simplify working with Promises.
Example:
async function fetchData() {
try {
const data = await promise;
console.log(data);
} catch (error) {
console.error(error);
}
}
fetchData(); // Outputs: Data fetched
Quiz Question
Q3: What is the purpose of the await keyword?
A. To pause the function until a Promise settles
B. To define an asynchronous function
C. To handle errors in Promises
D. To create a new Promise
Answer: A. To pause the function until a Promise settles
Object-Oriented Programming
JavaScript supports object-oriented programming (OOP) through prototypes and classes.
Classes
Classes are templates for creating objects.
Example:
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, ${this.name}!`);
}
}
const alice = new Person(‘Alice’);
alice.greet(); // Outputs: Hello, Alice!
Exercise
E3: Create a Car class with properties make, model, and a method displayInfo.
Solution:
class Car {
constructor(make, model) {
this.make = make;
this.model = model;
}
displayInfo() {
console.log(`Car: ${this.make} ${this.model}`);
}
}
const myCar = new Car(‘Toyota’, ‘Corolla’);
myCar.displayInfo(); // Outputs: Car: Toyota Corolla
Inheritance
Inheritance allows a class to inherit properties and methods from another class.
Example:
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(`${this.name} eats.`);
}
}
class Dog extends Animal {
bark() {
console.log(`${this.name} barks.`);
}
}
const dog = new Dog(‘Rex’);
dog.eat(); // Outputs: Rex eats.
dog.bark(); // Outputs: Rex barks.
Quiz Question
Q4: What keyword is used to inherit from another class?
A. extends
B. implements
C. inherits
D. super
Answer: A. extends
Encapsulation
Encapsulation restricts direct access to object components.
Example:
class BankAccount {
#balance;
constructor(initialBalance) {
this.#balance = initialBalance;
}
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // Outputs: 1500
Note: The # symbol denotes a private field.
Prototypes and Inheritance
JavaScript uses prototypes for inheritance.
Prototypal Inheritance
Every object has a prototype object from which it inherits methods and properties.
Example:
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
console.log(`Hello, ${this.name}!`);
};
const bob = new Person(‘Bob’);
bob.greet(); // Outputs: Hello, Bob!
Quiz Question
Q5: In JavaScript, which mechanism is used to implement inheritance?
A. Class-based inheritance
B. Prototypal inheritance
C. Interface-based inheritance
D. Multiple inheritance
Answer: B. Prototypal inheritance
Prototype Chain
Objects inherit properties and methods from their prototype, forming a chain.
Example:
console.log(bob.hasOwnProperty(‘name’)); // true
console.log(bob.hasOwnProperty(‘greet’)); // false
console.log(‘greet’ in bob); // true
Explanation:
- hasOwnProperty checks if the property exists on the object itself.
- The in operator checks the entire prototype chain.
Modern JavaScript Features
ES6 and beyond introduced new syntax and features.
ES6+ Syntax
- Let and Const: Block-scoped variables.
- Arrow Functions: Concise function syntax.
Example:
const numbers = [1, 2, 3];
const squared = numbers.map((n) => n * n);
console.log(squared); // Outputs: [1, 4, 9]
Modules
Modules allow code to be split into reusable pieces.
Exporting Module:
// math.js
export function add(a, b) {
return a + b;
}
Importing Module:
// main.js
import { add } from ‘./math.js’;
console.log(add(2, 3)); // Outputs: 5
Exercise
E4: Create a module that exports a function to calculate the factorial of a number.
Solution:
// factorial.js
export function factorial(n) {
return n <= 1 ? 1 : n * factorial(n – 1);
}
// main.js
import { factorial } from ‘./factorial.js’;
console.log(factorial(5)); // Outputs: 120
Destructuring
Destructuring allows unpacking values from arrays or properties from objects.
Example:
const person = { name: ‘Carol’, age: 28 };
const { name, age } = person;
console.log(name); // Outputs: Carol
Quiz Question
Q6: What will be the output?
const [a, , b] = [1, 2, 3];
console.log(a, b);
A. 1 2
B. 1 3
C. 2 3
D. Undefined
Answer: B. 1 3
Advanced Array Methods
Advanced methods provide powerful ways to manipulate arrays.
Map
Creates a new array by applying a function to each element.
Example:
const numbers = [1, 2, 3];
const doubled = numbers.map((n) => n * 2);
console.log(doubled); // Outputs: [2, 4, 6]
Filter
Creates a new array with elements that pass a test.
Example:
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter((n) => n % 2 === 0);
console.log(evens); // Outputs: [2, 4]
Reduce
Reduces the array to a single value.
Example:
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((total, n) => total + n, 0);
console.log(sum); // Outputs: 10
Exercise
E5: Use reduce to find the maximum number in an array.
Solution:
const numbers = [5, 1, 8, 3, 7];
const max = numbers.reduce((a, b) => (a > b ? a : b));
console.log(max); // Outputs: 8
Error Handling and Debugging
Proper error handling ensures your code is robust.
Try…Catch
Handles exceptions during code execution.
Example:
try {
throw new Error(‘Something went wrong’);
} catch (error) {
console.error(error.message); // Outputs: Something went wrong
}
Throwing Errors
You can throw custom errors.
Example:
function divide(a, b) {
if (b === 0) {
throw new Error(‘Divide by zero’);
}
return a / b;
}
try {
divide(4, 0);
} catch (error) {
console.error(error.message); // Outputs: Divide by zero
}
Quiz Question
Q7: What will be the output?
try {
console.log(‘Start’);
throw ‘An error’;
} catch (e) {
console.log(‘Caught: ‘ + e);
} finally {
console.log(‘Finally block’);
}
A. Start, Finally block
B. Start, Caught: An error
C. Start, Caught: An error, Finally block
D. Caught: An error, Finally block
Answer: C. Start, Caught: An error, Finally block
Debugging Techniques
- Breakpoints: Pause code execution at specific points.
- Console Logging: Output variable values to the console.
- Debugger Keyword: Triggers a breakpoint in the code.
JavaScript Design Patterns
Design patterns are proven solutions to common problems.
Singleton Pattern
Ensures a class has only one instance.
Example:
const Singleton = (function () {
let instance;
function createInstance() {
return new Object(‘I am the instance’);
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
},
};
})();
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // Outputs: true
Module Pattern
Encapsulates private and public methods.
Example:
const Module = (function () {
const privateVar = ‘I am private’;
function privateMethod() {
console.log(privateVar);
}
return {
publicMethod: function () {
privateMethod();
},
};
})();
Module.publicMethod(); // Outputs: I am private
Observer Pattern
Objects subscribe to events and get notified.
Example:
class Subject {
constructor() {
this.observers = [];
}
subscribe(fn) {
this.observers.push(fn);
}
notify(data) {
this.observers.forEach((fn) => fn(data));
}
}
const subject = new Subject();
subject.subscribe((data) => console.log(`Observer 1: ${data}`));
subject.subscribe((data) => console.log(`Observer 2: ${data}`));
subject.notify(‘Hello Observers’);
// Outputs:
// Observer 1: Hello Observers
// Observer 2: Hello Observers
Exercise
E6: Implement a simple Pub/Sub system.
Advanced DOM Manipulation
Manipulate the DOM efficiently.
Virtual DOM
An in-memory representation of the real DOM.
Example: Used in frameworks like React.
Event Delegation
Leverage event bubbling to handle events efficiently.
Example:
<ul id=”parent-list”>
<li id=”post-1″>Item 1</li>
<li id=”post-2″>Item 2</li>
<li id=”post-3″>Item 3</li>
</ul>
<script>
document.getElementById(‘parent-list’).addEventListener(‘click’, function (e) {
if (e.target && e.target.nodeName === ‘LI’) {
console.log(‘List item ‘, e.target.id.replace(‘post-‘, ”), ‘ was clicked.’);
}
});
</script>
Performance Optimization
Ensure your applications run smoothly.
Memory Management
Avoid memory leaks by:
- Removing event listeners when they’re no longer needed.
- Nullifying variables that hold large data structures.
Code Profiling
Use profiling tools to identify performance bottlenecks.
- Chrome DevTools: Provides a performance profiler.
- Node.js Profilers: For backend applications.
Quiz Question
Q8: Which of the following can cause a memory leak?
A. Unreferenced variables
B. Global variables
C. Closures
D. All of the above
Answer: D. All of the above
Conclusion
You’ve reached the end of this advanced JavaScript guide. We’ve covered complex topics like closures, asynchronous programming, object-oriented principles, design patterns, and performance optimization. Continue practicing and exploring these concepts to become a proficient JavaScript developer.
Additional Exercises
- Closure Exercise: Write a function that maintains a private counter and has methods to increment, decrement, and get the current value.
- Async/Await Exercise: Create a function that fetches data from an API using fetch and async/await.
Design Pattern Exercise: Implement the Factory pattern to create different types of objects based on input.