javascript
examples
examples.js⚡javascript
/**
* ========================================
* 8.2 CLASS INHERITANCE - CODE EXAMPLES
* ========================================
*/
/**
* EXAMPLE 1: Basic Inheritance with extends
* Child class inherits from parent class
*/
console.log('--- Example 1: Basic Inheritance ---');
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
move(distance) {
console.log(`${this.name} moved ${distance} meters`);
}
}
class Dog extends Animal {
bark() {
console.log(`${this.name} barks: Woof!`);
}
}
const dog = new Dog('Rex');
dog.speak(); // Inherited from Animal
dog.bark(); // Own method
dog.move(10); // Inherited from Animal
/**
* EXAMPLE 2: super() in Constructor
* Calling parent constructor
*/
console.log('\n--- Example 2: super() in Constructor ---');
class Vehicle {
constructor(make, model) {
this.make = make;
this.model = model;
this.speed = 0;
}
describe() {
return `${this.make} ${this.model}`;
}
}
class Car extends Vehicle {
constructor(make, model, doors = 4) {
// Must call super() before using 'this'
super(make, model);
this.doors = doors;
}
describe() {
return `${super.describe()} with ${this.doors} doors`;
}
}
const car = new Car('Toyota', 'Camry', 4);
console.log(car.describe()); // "Toyota Camry with 4 doors"
console.log(car.make); // "Toyota" (inherited property)
/**
* EXAMPLE 3: Method Overriding
* Child class overrides parent methods
*/
console.log('\n--- Example 3: Method Overriding ---');
class Shape {
constructor(color) {
this.color = color;
}
describe() {
return `A ${this.color} shape`;
}
area() {
throw new Error('area() must be implemented by subclass');
}
}
class Circle extends Shape {
constructor(color, radius) {
super(color);
this.radius = radius;
}
// Override describe
describe() {
return `A ${this.color} circle with radius ${this.radius}`;
}
// Implement abstract method
area() {
return Math.PI * this.radius ** 2;
}
}
class Rectangle extends Shape {
constructor(color, width, height) {
super(color);
this.width = width;
this.height = height;
}
describe() {
return `A ${this.color} rectangle (${this.width}x${this.height})`;
}
area() {
return this.width * this.height;
}
}
const circle = new Circle('red', 5);
const rect = new Rectangle('blue', 4, 6);
console.log(circle.describe()); // "A red circle with radius 5"
console.log(`Circle area: ${circle.area().toFixed(2)}`);
console.log(rect.describe()); // "A blue rectangle (4x6)"
console.log(`Rectangle area: ${rect.area()}`);
/**
* EXAMPLE 4: super.method() - Calling Parent Methods
* Extending parent behavior instead of replacing
*/
console.log('\n--- Example 4: super.method() ---');
class Logger {
log(message) {
console.log(`[LOG]: ${message}`);
}
error(message) {
console.error(`[ERROR]: ${message}`);
}
}
class TimestampLogger extends Logger {
log(message) {
const timestamp = new Date().toISOString();
super.log(`[${timestamp}] ${message}`);
}
error(message) {
const timestamp = new Date().toISOString();
super.error(`[${timestamp}] ${message}`);
}
}
class PrefixLogger extends TimestampLogger {
constructor(prefix) {
super();
this.prefix = prefix;
}
log(message) {
super.log(`${this.prefix}: ${message}`);
}
}
const logger = new PrefixLogger('APP');
logger.log('Application started');
/**
* EXAMPLE 5: Prototype Chain Verification
* Understanding the inheritance chain
*/
console.log('\n--- Example 5: Prototype Chain ---');
class A {
methodA() {
return 'A';
}
}
class B extends A {
methodB() {
return 'B';
}
}
class C extends B {
methodC() {
return 'C';
}
}
const c = new C();
// Verify prototype chain
console.log('c instanceof C:', c instanceof C); // true
console.log('c instanceof B:', c instanceof B); // true
console.log('c instanceof A:', c instanceof A); // true
console.log('c instanceof Object:', c instanceof Object); // true
// Prototype chain
console.log(Object.getPrototypeOf(c) === C.prototype); // true
console.log(Object.getPrototypeOf(C.prototype) === B.prototype); // true
console.log(Object.getPrototypeOf(B.prototype) === A.prototype); // true
// All methods accessible
console.log(c.methodA(), c.methodB(), c.methodC()); // "A" "B" "C"
/**
* EXAMPLE 6: Extending Built-in Array
* Creating custom array types
*/
console.log('\n--- Example 6: Extending Array ---');
class Stack extends Array {
peek() {
return this[this.length - 1];
}
isEmpty() {
return this.length === 0;
}
// Control what methods like map() return
static get [Symbol.species]() {
return Array;
}
}
const stack = new Stack();
stack.push(1, 2, 3, 4, 5);
console.log('Stack:', [...stack]); // [1, 2, 3, 4, 5]
console.log('Peek:', stack.peek()); // 5
console.log('Pop:', stack.pop()); // 5
console.log('isEmpty:', stack.isEmpty()); // false
// Inherited methods work
console.log(
'Filter:',
stack.filter((x) => x > 2)
); // [3, 4]
// Symbol.species means map returns Array, not Stack
const mapped = stack.map((x) => x * 2);
console.log('Mapped is Array:', mapped instanceof Array); // true
console.log('Mapped is Stack:', mapped instanceof Stack); // false
/**
* EXAMPLE 7: Extending Built-in Error
* Custom error types
*/
console.log('\n--- Example 7: Custom Errors ---');
class AppError extends Error {
constructor(message, code) {
super(message);
this.name = 'AppError';
this.code = code;
this.timestamp = new Date();
// Capture stack trace (V8 engines)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, AppError);
}
}
}
class ValidationError extends AppError {
constructor(message, field) {
super(message, 'VALIDATION_ERROR');
this.name = 'ValidationError';
this.field = field;
}
}
class NetworkError extends AppError {
constructor(message, statusCode) {
super(message, 'NETWORK_ERROR');
this.name = 'NetworkError';
this.statusCode = statusCode;
}
}
function handleError(error) {
if (error instanceof ValidationError) {
console.log(`Validation failed for '${error.field}': ${error.message}`);
} else if (error instanceof NetworkError) {
console.log(`Network error (${error.statusCode}): ${error.message}`);
} else if (error instanceof AppError) {
console.log(`App error [${error.code}]: ${error.message}`);
} else {
console.log(`Unknown error: ${error.message}`);
}
}
handleError(new ValidationError('Invalid email format', 'email'));
handleError(new NetworkError('Connection refused', 503));
/**
* EXAMPLE 8: Extending Map with Default Values
* Enhanced Map with default value support
*/
console.log('\n--- Example 8: Extending Map ---');
class DefaultMap extends Map {
constructor(defaultFactory, entries) {
super(entries);
this.defaultFactory = defaultFactory;
}
get(key) {
if (!this.has(key)) {
const defaultValue =
typeof this.defaultFactory === 'function'
? this.defaultFactory()
: this.defaultFactory;
this.set(key, defaultValue);
return defaultValue;
}
return super.get(key);
}
}
// Counter (default 0)
const counter = new DefaultMap(0);
counter.set('a', counter.get('a') + 1);
counter.set('a', counter.get('a') + 1);
counter.set('b', counter.get('b') + 1);
console.log('Counter a:', counter.get('a')); // 2
console.log('Counter b:', counter.get('b')); // 1
// Grouping (default empty array)
const groups = new DefaultMap(() => []);
groups.get('admins').push('Alice');
groups.get('admins').push('Bob');
groups.get('users').push('Charlie');
console.log('Groups:', Object.fromEntries(groups));
/**
* EXAMPLE 9: Abstract Base Class Pattern
* Simulating abstract classes
*/
console.log('\n--- Example 9: Abstract Base Class ---');
class AbstractRepository {
constructor() {
if (new.target === AbstractRepository) {
throw new Error('AbstractRepository cannot be instantiated directly');
}
}
// Abstract methods
findById(id) {
throw new Error('findById() must be implemented');
}
findAll() {
throw new Error('findAll() must be implemented');
}
save(entity) {
throw new Error('save() must be implemented');
}
delete(id) {
throw new Error('delete() must be implemented');
}
// Concrete method using abstract methods
exists(id) {
return this.findById(id) !== null;
}
}
class UserRepository extends AbstractRepository {
constructor() {
super();
this.users = new Map();
}
findById(id) {
return this.users.get(id) || null;
}
findAll() {
return [...this.users.values()];
}
save(user) {
this.users.set(user.id, user);
return user;
}
delete(id) {
return this.users.delete(id);
}
}
const userRepo = new UserRepository();
userRepo.save({ id: 1, name: 'Alice' });
userRepo.save({ id: 2, name: 'Bob' });
console.log('Find by ID:', userRepo.findById(1));
console.log('Exists:', userRepo.exists(1));
console.log('All users:', userRepo.findAll());
// This would throw:
// const abstract = new AbstractRepository(); // Error!
/**
* EXAMPLE 10: Multi-Level Inheritance
* Deep inheritance hierarchies
*/
console.log('\n--- Example 10: Multi-Level Inheritance ---');
class Entity {
constructor(id) {
this.id = id;
this.createdAt = new Date();
}
toString() {
return `Entity(${this.id})`;
}
}
class Person extends Entity {
constructor(id, name, email) {
super(id);
this.name = name;
this.email = email;
}
toString() {
return `Person(${this.id}, ${this.name})`;
}
}
class Employee extends Person {
constructor(id, name, email, department) {
super(id, name, email);
this.department = department;
}
toString() {
return `Employee(${this.id}, ${this.name}, ${this.department})`;
}
}
class Manager extends Employee {
constructor(id, name, email, department) {
super(id, name, email, department);
this.directReports = [];
}
addReport(employee) {
this.directReports.push(employee);
}
toString() {
return `Manager(${this.id}, ${this.name}, ${this.directReports.length} reports)`;
}
}
const manager = new Manager(1, 'Alice', 'alice@example.com', 'Engineering');
const emp1 = new Employee(2, 'Bob', 'bob@example.com', 'Engineering');
const emp2 = new Employee(3, 'Charlie', 'charlie@example.com', 'Engineering');
manager.addReport(emp1);
manager.addReport(emp2);
console.log(manager.toString()); // "Manager(1, Alice, 2 reports)"
console.log(manager instanceof Manager); // true
console.log(manager instanceof Employee); // true
console.log(manager instanceof Person); // true
console.log(manager instanceof Entity); // true
/**
* EXAMPLE 11: Overriding Getters and Setters
* Inheriting and extending accessors
*/
console.log('\n--- Example 11: Overriding Accessors ---');
class Product {
constructor(name, price) {
this.name = name;
this._price = price;
}
get price() {
return this._price;
}
set price(value) {
if (value < 0) throw new Error('Price cannot be negative');
this._price = value;
}
get displayPrice() {
return `$${this._price.toFixed(2)}`;
}
}
class DiscountedProduct extends Product {
constructor(name, price, discount) {
super(name, price);
this.discount = discount; // Percentage (0-100)
}
// Override getter to apply discount
get price() {
const discounted = super.price * (1 - this.discount / 100);
return Math.round(discounted * 100) / 100;
}
get originalPrice() {
return super.price;
}
get displayPrice() {
return `$${this.price.toFixed(2)} (was ${super.displayPrice})`;
}
}
const product = new Product('Widget', 100);
console.log(product.displayPrice); // "$100.00"
const discounted = new DiscountedProduct('Widget', 100, 20);
console.log(discounted.price); // 80
console.log(discounted.originalPrice); // 100
console.log(discounted.displayPrice); // "$80.00 (was $100.00)"
/**
* EXAMPLE 12: instanceof and Custom Checks
* Customizing instanceof behavior
*/
console.log('\n--- Example 12: instanceof Customization ---');
class Validator {
static [Symbol.hasInstance](obj) {
return (
obj !== null &&
typeof obj === 'object' &&
typeof obj.validate === 'function'
);
}
}
const validatable1 = {
validate() {
return true;
},
};
const validatable2 = {
check() {
return true;
},
};
console.log(validatable1 instanceof Validator); // true
console.log(validatable2 instanceof Validator); // false
// Useful for duck typing
class Iterable {
static [Symbol.hasInstance](obj) {
return obj != null && typeof obj[Symbol.iterator] === 'function';
}
}
console.log([1, 2, 3] instanceof Iterable); // true
console.log('hello' instanceof Iterable); // true
console.log({ a: 1 } instanceof Iterable); // false
/**
* EXAMPLE 13: Constructor Return Override
* Returning different object from constructor
*/
console.log('\n--- Example 13: Constructor Return Override ---');
class Singleton {
static instance = null;
constructor(name) {
if (Singleton.instance) {
return Singleton.instance;
}
this.name = name;
this.createdAt = new Date();
Singleton.instance = this;
}
}
const s1 = new Singleton('First');
const s2 = new Singleton('Second');
console.log(s1 === s2); // true
console.log(s1.name); // "First" (Second creation ignored)
// Extending singleton
class ChildSingleton extends Singleton {
constructor(name, value) {
super(name);
// Only sets if this is first instance
if (!this.value) {
this.value = value;
}
}
}
/**
* EXAMPLE 14: Method Resolution Order
* How methods are looked up in the chain
*/
console.log('\n--- Example 14: Method Resolution ---');
class Base {
method() {
return 'Base.method()';
}
static staticMethod() {
return 'Base.staticMethod()';
}
}
class Middle extends Base {
method() {
return `Middle.method() -> ${super.method()}`;
}
}
class Derived extends Middle {
method() {
return `Derived.method() -> ${super.method()}`;
}
}
const d = new Derived();
console.log(d.method());
// "Derived.method() -> Middle.method() -> Base.method()"
// Static method resolution
console.log(Derived.staticMethod()); // "Base.staticMethod()" (inherited)
/**
* EXAMPLE 15: Calling Different Parent Constructor
* Dynamic parent selection (mixin-like)
*/
console.log('\n--- Example 15: Dynamic Inheritance ---');
function createAnimalClass(BaseClass) {
return class extends BaseClass {
speak() {
console.log(`${this.name} makes a sound`);
}
};
}
class Named {
constructor(name) {
this.name = name;
}
}
class Identified {
constructor(id) {
this.id = id;
this.name = `Entity-${id}`;
}
}
const NamedAnimal = createAnimalClass(Named);
const IdentifiedAnimal = createAnimalClass(Identified);
const namedDog = new NamedAnimal('Rex');
const identifiedDog = new IdentifiedAnimal(123);
namedDog.speak(); // "Rex makes a sound"
identifiedDog.speak(); // "Entity-123 makes a sound"
/**
* EXAMPLE 16: Protected-like Members
* Simulating protected access
*/
console.log('\n--- Example 16: Protected Members ---');
// Convention: Use _ prefix for "protected" members
class Component {
constructor(name) {
this.name = name;
this._state = {}; // "Protected" - for subclasses
}
_updateState(newState) {
// "Protected" method
this._state = { ...this._state, ...newState };
this._onStateChange();
}
_onStateChange() {
// Hook for subclasses
}
getState() {
return { ...this._state };
}
}
class Counter extends Component {
constructor() {
super('Counter');
this._updateState({ count: 0 });
}
increment() {
this._updateState({ count: this._state.count + 1 });
}
decrement() {
this._updateState({ count: this._state.count - 1 });
}
_onStateChange() {
console.log(`Counter state: ${this._state.count}`);
}
}
const counter = new Counter();
counter.increment();
counter.increment();
counter.decrement();
/**
* EXAMPLE 17: Polymorphism Example
* Same interface, different implementations
*/
console.log('\n--- Example 17: Polymorphism ---');
class PaymentProcessor {
process(amount) {
throw new Error('process() must be implemented');
}
refund(transactionId) {
throw new Error('refund() must be implemented');
}
}
class CreditCardProcessor extends PaymentProcessor {
process(amount) {
console.log(`Processing $${amount} via Credit Card`);
return { success: true, transactionId: 'CC-' + Date.now() };
}
refund(transactionId) {
console.log(`Refunding transaction ${transactionId}`);
return { success: true };
}
}
class PayPalProcessor extends PaymentProcessor {
process(amount) {
console.log(`Processing $${amount} via PayPal`);
return { success: true, transactionId: 'PP-' + Date.now() };
}
refund(transactionId) {
console.log(`Refunding PayPal transaction ${transactionId}`);
return { success: true };
}
}
class CryptoProcessor extends PaymentProcessor {
process(amount) {
console.log(`Processing $${amount} in cryptocurrency`);
return { success: true, transactionId: 'CRYPTO-' + Date.now() };
}
refund(transactionId) {
console.log(`Crypto refund for ${transactionId}`);
return { success: true };
}
}
// Polymorphic function - works with any PaymentProcessor
function checkout(processor, amount) {
const result = processor.process(amount);
if (result.success) {
console.log(`Transaction ID: ${result.transactionId}`);
}
return result;
}
const processors = [
new CreditCardProcessor(),
new PayPalProcessor(),
new CryptoProcessor(),
];
for (const processor of processors) {
checkout(processor, 99.99);
}
/**
* EXAMPLE 18: Template Method Pattern
* Parent defines algorithm, children provide implementation
*/
console.log('\n--- Example 18: Template Method Pattern ---');
class DataExporter {
// Template method - defines the algorithm
export(data) {
const prepared = this.prepare(data);
const formatted = this.format(prepared);
const validated = this.validate(formatted);
return this.output(validated);
}
// Hook methods - can be overridden
prepare(data) {
return data;
}
validate(data) {
return data;
}
// Abstract methods - must be overridden
format(data) {
throw new Error('format() must be implemented');
}
output(data) {
throw new Error('output() must be implemented');
}
}
class JSONExporter extends DataExporter {
format(data) {
return JSON.stringify(data, null, 2);
}
output(data) {
console.log('JSON Output:', data);
return data;
}
}
class CSVExporter extends DataExporter {
prepare(data) {
// Ensure array format
return Array.isArray(data) ? data : [data];
}
format(data) {
if (data.length === 0) return '';
const headers = Object.keys(data[0]);
const rows = data.map((row) => headers.map((h) => row[h]).join(','));
return [headers.join(','), ...rows].join('\n');
}
output(data) {
console.log('CSV Output:\n' + data);
return data;
}
}
const data = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 },
];
const jsonExporter = new JSONExporter();
const csvExporter = new CSVExporter();
jsonExporter.export(data);
csvExporter.export(data);
/**
* EXAMPLE 19: Complete Inheritance Example
* Real-world scenario with full inheritance chain
*/
console.log('\n--- Example 19: Complete Example ---');
class EventEmitter {
constructor() {
this._events = new Map();
}
on(event, callback) {
if (!this._events.has(event)) {
this._events.set(event, []);
}
this._events.get(event).push(callback);
return this;
}
emit(event, ...args) {
if (this._events.has(event)) {
for (const callback of this._events.get(event)) {
callback(...args);
}
}
return this;
}
}
class Collection extends EventEmitter {
constructor() {
super();
this._items = [];
}
add(item) {
this._items.push(item);
this.emit('add', item);
return this;
}
remove(item) {
const index = this._items.indexOf(item);
if (index > -1) {
this._items.splice(index, 1);
this.emit('remove', item);
}
return this;
}
get items() {
return [...this._items];
}
get length() {
return this._items.length;
}
}
class UniqueCollection extends Collection {
add(item) {
if (!this._items.includes(item)) {
super.add(item);
}
return this;
}
}
class SortedCollection extends Collection {
constructor(compareFn = (a, b) => a - b) {
super();
this._compareFn = compareFn;
}
add(item) {
super.add(item);
this._items.sort(this._compareFn);
return this;
}
}
// Usage
const unique = new UniqueCollection();
unique.on('add', (item) => console.log(`Added: ${item}`));
unique.add(1).add(2).add(1).add(3);
console.log('Unique items:', unique.items); // [1, 2, 3]
const sorted = new SortedCollection();
sorted.add(3).add(1).add(2);
console.log('Sorted items:', sorted.items); // [1, 2, 3]
/**
* EXAMPLE 20: Mixin-like Inheritance
* Combining behaviors from multiple sources
*/
console.log('\n--- Example 20: Mixin-like Inheritance ---');
// Mixin factories
const Timestamped = (Base) =>
class extends Base {
constructor(...args) {
super(...args);
this.createdAt = new Date();
this.updatedAt = new Date();
}
touch() {
this.updatedAt = new Date();
return this;
}
};
const Serializable = (Base) =>
class extends Base {
toJSON() {
return { ...this };
}
toObject() {
return Object.fromEntries(
Object.entries(this).filter(([key]) => !key.startsWith('_'))
);
}
};
const Validatable = (Base) =>
class extends Base {
validate() {
const errors = [];
for (const [field, rules] of Object.entries(
this.constructor.validationRules || {}
)) {
for (const rule of rules) {
const error = rule(this[field], field);
if (error) errors.push(error);
}
}
return { valid: errors.length === 0, errors };
}
};
// Compose mixins
class BaseModel {
constructor(data) {
Object.assign(this, data);
}
}
class User extends Validatable(Serializable(Timestamped(BaseModel))) {
static validationRules = {
name: [
(v, f) => (!v ? `${f} is required` : null),
(v, f) => (v && v.length < 2 ? `${f} too short` : null),
],
email: [(v, f) => (!v ? `${f} is required` : null)],
};
}
const user = new User({ name: 'Alice', email: 'alice@example.com' });
console.log('User created at:', user.createdAt);
console.log('Validation:', user.validate());
console.log('JSON:', user.toJSON());
user.touch();
console.log('Updated at:', user.updatedAt);
console.log('\n========================================');
console.log('End of Class Inheritance Examples');
console.log('========================================');