javascript
examples
examples.js⚡javascript
/**
* ========================================
* 8.1 CLASS BASICS - CODE EXAMPLES
* ========================================
*/
/**
* EXAMPLE 1: Basic Class Declaration
* Simple class with constructor and methods
*/
console.log('--- Example 1: Basic Class Declaration ---');
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hello, I'm ${this.name}`;
}
introduce() {
return `I'm ${this.name}, ${this.age} years old`;
}
}
const alice = new Person('Alice', 30);
console.log(alice.greet()); // "Hello, I'm Alice"
console.log(alice.introduce()); // "I'm Alice, 30 years old"
/**
* EXAMPLE 2: Class Type and Nature
* Classes are functions under the hood
*/
console.log('\n--- Example 2: Class Type ---');
class Example {}
console.log(typeof Example); // "function"
console.log(Example.prototype.constructor === Example); // true
console.log(Example === Example.prototype.constructor); // true
// Class is NOT hoisted like function declarations
// console.log(new NotYetDefined()); // ReferenceError
// class NotYetDefined {}
/**
* EXAMPLE 3: Constructor with Default Parameters
* Using defaults and options object
*/
console.log('\n--- Example 3: Constructor Defaults ---');
class Config {
constructor({
host = 'localhost',
port = 3000,
debug = false,
timeout = 5000,
} = {}) {
this.host = host;
this.port = port;
this.debug = debug;
this.timeout = timeout;
}
getUrl() {
return `http://${this.host}:${this.port}`;
}
}
const defaultConfig = new Config();
console.log(defaultConfig.getUrl()); // "http://localhost:3000"
const customConfig = new Config({ port: 8080, debug: true });
console.log(customConfig.port); // 8080
console.log(customConfig.debug); // true
/**
* EXAMPLE 4: Instance Methods
* Methods are shared via prototype
*/
console.log('\n--- Example 4: Instance Methods ---');
class Counter {
constructor(initial = 0) {
this.count = initial;
}
increment() {
this.count++;
return this;
}
decrement() {
this.count--;
return this;
}
reset() {
this.count = 0;
return this;
}
getCount() {
return this.count;
}
}
const counter1 = new Counter(5);
const counter2 = new Counter(10);
// Methods are shared
console.log(counter1.increment === counter2.increment); // true
// Chaining works
counter1.increment().increment().increment();
console.log(counter1.getCount()); // 8
/**
* EXAMPLE 5: Getter and Setter
* Controlled property access
*/
console.log('\n--- Example 5: Getters and Setters ---');
class Temperature {
constructor(celsius = 0) {
this._celsius = celsius;
}
// Getter
get celsius() {
return this._celsius;
}
// Setter with validation
set celsius(value) {
if (value < -273.15) {
throw new RangeError('Temperature below absolute zero');
}
this._celsius = value;
}
// Computed getters
get fahrenheit() {
return (this._celsius * 9) / 5 + 32;
}
set fahrenheit(value) {
this._celsius = ((value - 32) * 5) / 9;
}
get kelvin() {
return this._celsius + 273.15;
}
set kelvin(value) {
this._celsius = value - 273.15;
}
}
const temp = new Temperature(25);
console.log(`${temp.celsius}°C = ${temp.fahrenheit}°F = ${temp.kelvin}K`);
temp.fahrenheit = 100;
console.log(`${temp.celsius.toFixed(2)}°C`); // "37.78°C"
/**
* EXAMPLE 6: Read-Only Properties (Getter Only)
* Properties that can't be set
*/
console.log('\n--- Example 6: Read-Only Properties ---');
class Circle {
constructor(radius) {
this._radius = radius;
}
get radius() {
return this._radius;
}
// No setter - read-only
get diameter() {
return this._radius * 2;
}
get circumference() {
return 2 * Math.PI * this._radius;
}
get area() {
return Math.PI * this._radius ** 2;
}
}
const circle = new Circle(5);
console.log(`Radius: ${circle.radius}`);
console.log(`Diameter: ${circle.diameter}`);
console.log(`Circumference: ${circle.circumference.toFixed(2)}`);
console.log(`Area: ${circle.area.toFixed(2)}`);
// Attempting to set read-only property
// circle.radius = 10; // Fails silently (or throws in strict mode)
/**
* EXAMPLE 7: Computed Method Names
* Dynamic method names using expressions
*/
console.log('\n--- Example 7: Computed Method Names ---');
const PREFIX = 'get';
const SUFFIX = 'Value';
class DynamicMethods {
constructor() {
this.data = { x: 10, y: 20 };
}
// Computed method name
[`${PREFIX}${SUFFIX}`]() {
return this.data;
}
// Using variable
[Symbol.toStringTag] = 'DynamicMethods';
// Symbol.iterator
*[Symbol.iterator]() {
for (const key in this.data) {
yield [key, this.data[key]];
}
}
}
const dynamic = new DynamicMethods();
console.log(dynamic.getValue()); // { x: 10, y: 20 }
console.log([...dynamic]); // [["x", 10], ["y", 20]]
console.log(Object.prototype.toString.call(dynamic)); // "[object DynamicMethods]"
/**
* EXAMPLE 8: Class Expressions
* Anonymous and named class expressions
*/
console.log('\n--- Example 8: Class Expressions ---');
// Anonymous class expression
const Animal = class {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
};
// Named class expression
const Dog = class DogClass {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} barks`);
}
// Can use DogClass inside the class
clone() {
return new DogClass(this.name);
}
};
const animal = new Animal('Generic');
animal.speak(); // "Generic makes a sound"
const dog = new Dog('Rex');
dog.speak(); // "Rex barks"
const dogClone = dog.clone();
console.log(dogClone.name); // "Rex"
/**
* EXAMPLE 9: Class as First-Class Citizen
* Passing classes as arguments
*/
console.log('\n--- Example 9: Class as First-Class Citizen ---');
function createInstance(Class, ...args) {
return new Class(...args);
}
function registerClass(Class, registry) {
registry[Class.name] = Class;
return Class;
}
class User {
constructor(name) {
this.name = name;
}
}
class Product {
constructor(name, price) {
this.name = name;
this.price = price;
}
}
const user = createInstance(User, 'Alice');
const product = createInstance(Product, 'Widget', 29.99);
console.log(user); // User { name: "Alice" }
console.log(product); // Product { name: "Widget", price: 29.99 }
const registry = {};
registerClass(User, registry);
registerClass(Product, registry);
console.log(Object.keys(registry)); // ["User", "Product"]
/**
* EXAMPLE 10: Immediately Invoked Class Expression
* Creating singleton-like objects
*/
console.log('\n--- Example 10: IICE (Singleton) ---');
const AppState = new (class {
constructor() {
this.state = {};
this.listeners = [];
}
get(key) {
return this.state[key];
}
set(key, value) {
this.state[key] = value;
this.notify(key, value);
}
subscribe(callback) {
this.listeners.push(callback);
return () => {
const index = this.listeners.indexOf(callback);
if (index > -1) this.listeners.splice(index, 1);
};
}
notify(key, value) {
for (const listener of this.listeners) {
listener(key, value);
}
}
})();
AppState.subscribe((key, value) => {
console.log(`State changed: ${key} = ${value}`);
});
AppState.set('user', 'Alice'); // "State changed: user = Alice"
console.log(AppState.get('user')); // "Alice"
/**
* EXAMPLE 11: Method Chaining Pattern
* Fluent interface design
*/
console.log('\n--- Example 11: Method Chaining ---');
class StringBuilder {
constructor(initial = '') {
this.value = initial;
}
append(str) {
this.value += str;
return this;
}
prepend(str) {
this.value = str + this.value;
return this;
}
wrap(prefix, suffix) {
this.value = prefix + this.value + suffix;
return this;
}
uppercase() {
this.value = this.value.toUpperCase();
return this;
}
lowercase() {
this.value = this.value.toLowerCase();
return this;
}
trim() {
this.value = this.value.trim();
return this;
}
toString() {
return this.value;
}
}
const result = new StringBuilder('hello')
.append(' world')
.uppercase()
.wrap('[', ']')
.toString();
console.log(result); // "[HELLO WORLD]"
/**
* EXAMPLE 12: Validation in Constructor
* Ensuring valid object creation
*/
console.log('\n--- Example 12: Constructor Validation ---');
class Email {
constructor(address) {
if (!address || typeof address !== 'string') {
throw new TypeError('Email address must be a string');
}
if (!Email.isValid(address)) {
throw new Error(`Invalid email format: ${address}`);
}
this.address = address.toLowerCase();
this.domain = this.address.split('@')[1];
}
static isValid(address) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(address);
}
toString() {
return this.address;
}
}
try {
const email = new Email('Alice@Example.COM');
console.log(email.address); // "alice@example.com"
console.log(email.domain); // "example.com"
// const invalid = new Email("not-an-email"); // Throws Error
} catch (e) {
console.error(e.message);
}
/**
* EXAMPLE 13: this Binding in Methods
* Understanding method context
*/
console.log('\n--- Example 13: this Binding ---');
class Button {
constructor(label) {
this.label = label;
this.clickCount = 0;
// Option 1: Bind in constructor
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.clickCount++;
console.log(`${this.label} clicked ${this.clickCount} times`);
}
// Option 2: Arrow function property (requires class fields)
// handleClick = () => {
// this.clickCount++;
// console.log(`${this.label} clicked ${this.clickCount} times`);
// };
}
const button = new Button('Submit');
// Direct call - works
button.handleClick(); // "Submit clicked 1 times"
// As callback - works because of bind
const callback = button.handleClick;
callback(); // "Submit clicked 2 times" (would fail without bind)
/**
* EXAMPLE 14: Generator Methods
* Iterators in classes
*/
console.log('\n--- Example 14: Generator Methods ---');
class Range {
constructor(start, end, step = 1) {
this.start = start;
this.end = end;
this.step = step;
}
*[Symbol.iterator]() {
for (let i = this.start; i <= this.end; i += this.step) {
yield i;
}
}
*reversed() {
for (let i = this.end; i >= this.start; i -= this.step) {
yield i;
}
}
}
const range = new Range(1, 5);
console.log([...range]); // [1, 2, 3, 4, 5]
console.log([...range.reversed()]); // [5, 4, 3, 2, 1]
const evenRange = new Range(0, 10, 2);
console.log([...evenRange]); // [0, 2, 4, 6, 8, 10]
/**
* EXAMPLE 15: Async Methods
* Asynchronous operations in classes
*/
console.log('\n--- Example 15: Async Methods ---');
class DataFetcher {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.cache = new Map();
}
async fetch(endpoint) {
const url = `${this.baseUrl}${endpoint}`;
if (this.cache.has(url)) {
console.log('Returning cached data');
return this.cache.get(url);
}
console.log(`Fetching: ${url}`);
// Simulate API call
const data = await this.simulateFetch(endpoint);
this.cache.set(url, data);
return data;
}
// Simulate fetch for demo
async simulateFetch(endpoint) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ endpoint, timestamp: Date.now() });
}, 100);
});
}
clearCache() {
this.cache.clear();
}
}
(async () => {
const fetcher = new DataFetcher('https://api.example.com');
const data1 = await fetcher.fetch('/users');
console.log(data1);
const data2 = await fetcher.fetch('/users'); // From cache
console.log(data1 === data2); // true
})();
/**
* EXAMPLE 16: Class with Symbol Properties
* Using symbols for special behavior
*/
console.log('\n--- Example 16: Symbol Properties ---');
class Collection {
constructor(items = []) {
this.items = [...items];
}
// Make it iterable
*[Symbol.iterator]() {
yield* this.items;
}
// Custom string tag
get [Symbol.toStringTag]() {
return 'Collection';
}
// Custom primitive conversion
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return this.items.length;
}
if (hint === 'string') {
return `Collection(${this.items.join(', ')})`;
}
return this.items;
}
add(item) {
this.items.push(item);
return this;
}
get length() {
return this.items.length;
}
}
const collection = new Collection([1, 2, 3]);
console.log([...collection]); // [1, 2, 3]
console.log(String(collection)); // "Collection(1, 2, 3)"
console.log(+collection); // 3
console.log(Object.prototype.toString.call(collection)); // "[object Collection]"
/**
* EXAMPLE 17: Multiple Constructors Pattern
* Factory methods for different creation patterns
*/
console.log('\n--- Example 17: Multiple Constructors ---');
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
// Alternative constructor from polar coordinates
static fromPolar(r, theta) {
return new Point(r * Math.cos(theta), r * Math.sin(theta));
}
// From array
static fromArray([x, y]) {
return new Point(x, y);
}
// From object
static fromObject({ x, y }) {
return new Point(x, y);
}
// Clone
static from(point) {
return new Point(point.x, point.y);
}
distanceTo(other) {
return Math.sqrt((this.x - other.x) ** 2 + (this.y - other.y) ** 2);
}
toString() {
return `(${this.x}, ${this.y})`;
}
}
const p1 = new Point(3, 4);
const p2 = Point.fromPolar(5, Math.PI / 4);
const p3 = Point.fromArray([1, 2]);
const p4 = Point.fromObject({ x: 5, y: 6 });
console.log(p1.toString()); // "(3, 4)"
console.log(p2.toString()); // "(3.54, 3.54)" approximately
console.log(p1.distanceTo(p4)); // ~2.83
/**
* EXAMPLE 18: Class Comparison to Constructor Function
* Side-by-side comparison
*/
console.log('\n--- Example 18: Class vs Constructor ---');
// ES6 Class
class PersonClass {
constructor(name) {
this.name = name;
}
greet() {
return `Hello from ${this.name}`;
}
}
// Equivalent Constructor Function
function PersonFunction(name) {
this.name = name;
}
PersonFunction.prototype.greet = function () {
return `Hello from ${this.name}`;
};
const classInstance = new PersonClass('Alice');
const funcInstance = new PersonFunction('Bob');
console.log(classInstance.greet()); // "Hello from Alice"
console.log(funcInstance.greet()); // "Hello from Bob"
// Key differences
console.log('\nDifferences:');
// 1. Methods enumerable
for (const key in classInstance) {
console.log(`Class enumerable: ${key}`); // Only "name"
}
for (const key in funcInstance) {
console.log(`Function enumerable: ${key}`); // "name" AND "greet"
}
// 2. Calling without new
try {
PersonClass('Test'); // Throws TypeError
} catch (e) {
console.log('Class without new: TypeError');
}
const result2 = PersonFunction('Test'); // Returns undefined, creates global
console.log("Function without new: Works (but shouldn't)");
/**
* EXAMPLE 19: Complex Class Example
* Full-featured class demonstration
*/
console.log('\n--- Example 19: Complex Class Example ---');
class BankAccount {
constructor(accountNumber, holder, initialBalance = 0) {
this._accountNumber = accountNumber;
this._holder = holder;
this._balance = initialBalance;
this._transactions = [];
this._log('Account created');
}
// Getters
get accountNumber() {
return this._accountNumber;
}
get holder() {
return this._holder;
}
get balance() {
return this._balance;
}
get transactions() {
return [...this._transactions]; // Return copy
}
// Methods
deposit(amount) {
if (amount <= 0) {
throw new Error('Deposit amount must be positive');
}
this._balance += amount;
this._log(`Deposit: +${amount}`);
return this;
}
withdraw(amount) {
if (amount <= 0) {
throw new Error('Withdrawal amount must be positive');
}
if (amount > this._balance) {
throw new Error('Insufficient funds');
}
this._balance -= amount;
this._log(`Withdrawal: -${amount}`);
return this;
}
transfer(toAccount, amount) {
this.withdraw(amount);
toAccount.deposit(amount);
this._log(`Transfer to ${toAccount.accountNumber}: -${amount}`);
return this;
}
// Private helper
_log(message) {
this._transactions.push({
message,
timestamp: new Date(),
balance: this._balance,
});
}
// String representation
toString() {
return `Account ${this._accountNumber} (${this._holder}): $${this._balance}`;
}
}
const checking = new BankAccount('CHK001', 'Alice', 1000);
const savings = new BankAccount('SAV001', 'Alice', 5000);
checking.deposit(500).withdraw(200);
savings.transfer(checking, 1000);
console.log(checking.toString()); // Account CHK001 (Alice): $2300
console.log(`Transactions: ${checking.transactions.length}`);
/**
* EXAMPLE 20: Class with Validation and Events
* Combining patterns
*/
console.log('\n--- Example 20: Advanced Class Pattern ---');
class Form {
constructor(fields = {}) {
this._fields = {};
this._errors = {};
this._listeners = new Map();
for (const [name, config] of Object.entries(fields)) {
this.addField(name, config);
}
}
addField(name, { value = '', validators = [] } = {}) {
this._fields[name] = { value, validators };
return this;
}
get(name) {
return this._fields[name]?.value;
}
set(name, value) {
if (!this._fields[name]) {
throw new Error(`Unknown field: ${name}`);
}
const oldValue = this._fields[name].value;
this._fields[name].value = value;
this._emit('change', { field: name, oldValue, newValue: value });
return this;
}
validate() {
this._errors = {};
let isValid = true;
for (const [name, field] of Object.entries(this._fields)) {
const fieldErrors = [];
for (const validator of field.validators) {
const error = validator(field.value);
if (error) fieldErrors.push(error);
}
if (fieldErrors.length > 0) {
this._errors[name] = fieldErrors;
isValid = false;
}
}
this._emit('validate', { valid: isValid, errors: this._errors });
return isValid;
}
get errors() {
return { ...this._errors };
}
get data() {
return Object.fromEntries(
Object.entries(this._fields).map(([k, v]) => [k, v.value])
);
}
on(event, callback) {
if (!this._listeners.has(event)) {
this._listeners.set(event, []);
}
this._listeners.get(event).push(callback);
return this;
}
_emit(event, data) {
if (this._listeners.has(event)) {
for (const callback of this._listeners.get(event)) {
callback(data);
}
}
}
}
// Validators
const required = (value) => (!value ? 'This field is required' : null);
const email = (value) =>
value && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
? 'Invalid email format'
: null;
const minLength = (min) => (value) =>
value && value.length < min ? `Minimum ${min} characters` : null;
// Usage
const form = new Form({
name: { value: '', validators: [required, minLength(2)] },
email: { value: '', validators: [required, email] },
});
form.on('change', ({ field, newValue }) => {
console.log(`Field "${field}" changed to "${newValue}"`);
});
form.on('validate', ({ valid, errors }) => {
console.log(`Form valid: ${valid}`);
if (!valid) console.log('Errors:', errors);
});
form.set('name', 'A');
form.set('email', 'invalid-email');
form.validate();
// Form valid: false
// Errors: { name: ["Minimum 2 characters"], email: ["Invalid email format"] }
form.set('name', 'Alice');
form.set('email', 'alice@example.com');
form.validate();
// Form valid: true
console.log('Form data:', form.data);
console.log('\n========================================');
console.log('End of Class Basics Examples');
console.log('========================================');