Class Syntax and OOP
Understanding ES6 classes and object-oriented programming in JavaScript
Challenge
Section titled “Challenge”You need to create reusable, maintainable code using object-oriented principles. How do you use ES6 classes effectively while avoiding common pitfalls?
Explanation
Section titled “Explanation”ES6 classes provide syntactic sugar over JavaScript’s prototype-based inheritance. While they compile to the same prototype model, they offer a cleaner, more familiar syntax for developers from other OOP languages.
Basic Class Syntax
Section titled “Basic Class Syntax”class Animal { constructor(name) { this.name = name; }
speak() { console.log(`${this.name} makes a sound`); }
// Static methods static info() { return 'This is an animal class'; }}
const dog = new Animal('Rex');dog.speak(); // Rex makes a soundconsole.log(Animal.info()); // This is an animal class
// Classes are functionsconsole.log(typeof Animal); // functionInheritance and super
Section titled “Inheritance and super”class Animal { constructor(name) { this.name = name; }
eat(food) { console.log(`${this.name} eats ${food}`); }}
class Dog extends Animal { constructor(name, breed) { super(name); // Call parent constructor this.breed = breed; }
bark() { console.log('Woof!'); }
// Override parent method eat(food) { super.eat(food); // Call parent method console.log('Yummy!'); }}
const dog = new Dog('Buddy', 'Labrador');dog.eat('food');// "Buddy eats food"// "Yummy!"Getters and Setters
Section titled “Getters and Setters”class User { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this._age = 0; }
// Getter get fullName() { return `${this.firstName} ${this.lastName}`; }
// Setter set fullName(name) { [this.firstName, this.lastName] = name.split(' '); }
get age() { return this._age; }
set age(value) { if (value < 0 || value > 150) { console.log('Invalid age'); return; } this._age = value; }}
const user = new User('John', 'Doe');console.log(user.fullName); // John Doe
user.fullName = 'Jane Smith';console.log(user.firstName); // Jane
user.age = 30;console.log(user.age); // 30
user.age = 200; // Logs: Invalid agePrivate Fields and Methods
Section titled “Private Fields and Methods”class BankAccount { #balance = 0; // Private field
constructor(initialBalance) { this.#balance = initialBalance; }
// Private method #calculateInterest() { return this.#balance * 0.05; }
// Public method deposit(amount) { this.#balance += amount; return this.#balance; }
getBalance() { return this.#balance; }
getInterest() { return this.#calculateInterest(); }}
const account = new BankAccount(1000);account.deposit(500);console.log(account.getBalance()); // 1500
// Can't access private fieldsconsole.log(account.#balance); // SyntaxErrorconsole.log(account._balance); // undefinedStatic Methods and Properties
Section titled “Static Methods and Properties”class Logger { static #instances = 0; static level = 'info';
constructor(name) { this.name = name; Logger.#instances++; }
static getInstance(name) { return new Logger(name); }
static getInstanceCount() { return Logger.#instances; }
static setLevel(level) { Logger.level = level; }
log(message) { console.log(`[${this.name}] ${message}`); }}
const log1 = Logger.getInstance('App');const log2 = Logger.getInstance('Database');
console.log(Logger.getInstanceCount()); // 2Logger.setLevel('debug');Abstract Classes Pattern
Section titled “Abstract Classes Pattern”class Database { constructor() { // Prevent instantiation if (this.constructor === Database) { throw new Error('Cannot instantiate abstract class'); } }
// Abstract method query(sql) { throw new Error('query() must be implemented'); }
// Concrete method close() { console.log('Closing connection'); }}
class PostgreSQL extends Database { query(sql) { // Implementation console.log(`Executing PostgreSQL: ${sql}`); }}
// const db = new Database(); // Error!const pgDb = new PostgreSQL();pgDb.query('SELECT * FROM users');Practical Example: Event Emitter
Section titled “Practical Example: Event Emitter”class EventEmitter { constructor() { this.events = {}; }
on(event, listener) { if (!this.events[event]) { this.events[event] = []; } this.events[event].push(listener); }
emit(event, ...args) { if (!this.events[event]) return;
this.events[event].forEach(listener => { listener(...args); }); }
off(event, listenerToRemove) { if (!this.events[event]) return;
this.events[event] = this.events[event].filter( listener => listener !== listenerToRemove ); }}
class Button extends EventEmitter { click() { this.emit('click', { timestamp: Date.now() }); }}
const button = new Button();button.on('click', (data) => console.log('Clicked at', data.timestamp));button.click();