javascript
examples
examples.js⚡javascript
/**
* ========================================
* 7.6 OBJECT PATTERNS - CODE EXAMPLES
* ========================================
*/
/**
* EXAMPLE 1: Basic Factory Pattern
* Creates objects without using `new`
*/
console.log('--- Example 1: Basic Factory Pattern ---');
function createPerson(name, age) {
return {
name,
age,
greet() {
return `Hi, I'm ${this.name}`;
},
};
}
const alice = createPerson('Alice', 30);
const bob = createPerson('Bob', 25);
console.log(alice.greet()); // "Hi, I'm Alice"
console.log(bob.greet()); // "Hi, I'm Bob"
/**
* EXAMPLE 2: Factory with Type Selection
* Returns different object types based on input
*/
console.log('\n--- Example 2: Factory with Type Selection ---');
function createShape(type, options) {
const shapes = {
circle: () => ({
type: 'circle',
radius: options.radius,
area() {
return Math.PI * this.radius ** 2;
},
}),
rectangle: () => ({
type: 'rectangle',
width: options.width,
height: options.height,
area() {
return this.width * this.height;
},
}),
triangle: () => ({
type: 'triangle',
base: options.base,
height: options.height,
area() {
return 0.5 * this.base * this.height;
},
}),
};
if (!shapes[type]) {
throw new Error(`Unknown shape: ${type}`);
}
return shapes[type]();
}
const circle = createShape('circle', { radius: 5 });
const rect = createShape('rectangle', { width: 4, height: 6 });
console.log(`Circle area: ${circle.area().toFixed(2)}`); // ~78.54
console.log(`Rectangle area: ${rect.area()}`); // 24
/**
* EXAMPLE 3: Constructor Pattern with Prototype
* Uses `new` keyword and shared methods
*/
console.log('\n--- Example 3: Constructor Pattern ---');
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
this.running = false;
}
Car.prototype.start = function () {
this.running = true;
console.log(`${this.make} ${this.model} started`);
return this;
};
Car.prototype.stop = function () {
this.running = false;
console.log(`${this.make} ${this.model} stopped`);
return this;
};
Car.prototype.info = function () {
return `${this.year} ${this.make} ${this.model}`;
};
const myCar = new Car('Toyota', 'Camry', 2020);
myCar.start().stop();
console.log(myCar.info()); // "2020 Toyota Camry"
/**
* EXAMPLE 4: Constructor with Private State
* Uses closures for encapsulation
*/
console.log('\n--- Example 4: Constructor with Private State ---');
function Counter(initial = 0) {
// Private variable
let count = initial;
// Privileged methods (can access private state)
this.increment = function () {
count++;
return this;
};
this.decrement = function () {
count--;
return this;
};
this.getCount = function () {
return count;
};
this.reset = function () {
count = initial;
return this;
};
}
const counter = new Counter(10);
counter.increment().increment().increment();
console.log(counter.getCount()); // 13
console.log(counter.count); // undefined (private!)
/**
* EXAMPLE 5: Classic Module Pattern (IIFE)
* Encapsulates private state with public API
*/
console.log('\n--- Example 5: Module Pattern ---');
const Calculator = (function () {
// Private state
let result = 0;
let history = [];
// Private function
function addToHistory(operation) {
history.push({
operation,
result,
timestamp: new Date(),
});
}
// Public API
return {
add(n) {
result += n;
addToHistory(`add ${n}`);
return this;
},
subtract(n) {
result -= n;
addToHistory(`subtract ${n}`);
return this;
},
multiply(n) {
result *= n;
addToHistory(`multiply ${n}`);
return this;
},
divide(n) {
if (n === 0) throw new Error('Division by zero');
result /= n;
addToHistory(`divide ${n}`);
return this;
},
getResult() {
return result;
},
getHistory() {
return [...history]; // Return copy
},
reset() {
result = 0;
history = [];
return this;
},
};
})();
Calculator.add(10).multiply(2).subtract(5);
console.log(Calculator.getResult()); // 15
console.log(Calculator.getHistory().length); // 3
/**
* EXAMPLE 6: Revealing Module Pattern
* All functions private, then revealed
*/
console.log('\n--- Example 6: Revealing Module Pattern ---');
const TaskManager = (function () {
const tasks = [];
function generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
function addTask(title, priority = 'normal') {
const task = {
id: generateId(),
title,
priority,
completed: false,
createdAt: new Date(),
};
tasks.push(task);
return task;
}
function completeTask(id) {
const task = tasks.find((t) => t.id === id);
if (task) {
task.completed = true;
task.completedAt = new Date();
}
return task;
}
function removeTask(id) {
const index = tasks.findIndex((t) => t.id === id);
if (index > -1) {
return tasks.splice(index, 1)[0];
}
return null;
}
function getPendingTasks() {
return tasks.filter((t) => !t.completed);
}
function getAllTasks() {
return [...tasks];
}
// Reveal public API
return {
add: addTask,
complete: completeTask,
remove: removeTask,
getPending: getPendingTasks,
getAll: getAllTasks,
};
})();
const task1 = TaskManager.add('Learn JavaScript', 'high');
const task2 = TaskManager.add('Build project');
TaskManager.complete(task1.id);
console.log(TaskManager.getPending().length); // 1
console.log(TaskManager.getAll().length); // 2
/**
* EXAMPLE 7: Singleton Pattern (Object Literal)
* Simple singleton with object literal
*/
console.log('\n--- Example 7: Object Literal Singleton ---');
const AppConfig = {
version: '1.0.0',
apiUrl: 'https://api.example.com',
timeout: 5000,
debug: false,
set(key, value) {
if (Object.prototype.hasOwnProperty.call(this, key)) {
this[key] = value;
}
return this;
},
get(key) {
return this[key];
},
};
// Prevent modification (optional)
// Object.freeze(AppConfig);
console.log(AppConfig.get('apiUrl'));
AppConfig.set('debug', true);
console.log(AppConfig.debug); // true
/**
* EXAMPLE 8: Lazy Singleton Pattern
* Creates instance only when first needed
*/
console.log('\n--- Example 8: Lazy Singleton ---');
const DatabaseConnection = (function () {
let instance = null;
function createConnection() {
console.log('Creating database connection...');
return {
host: 'localhost',
port: 5432,
connected: false,
connect() {
this.connected = true;
console.log('Connected to database');
return this;
},
disconnect() {
this.connected = false;
console.log('Disconnected from database');
return this;
},
query(sql) {
if (!this.connected) {
throw new Error('Not connected to database');
}
console.log(`Executing: ${sql}`);
return [];
},
};
}
return {
getInstance() {
if (!instance) {
instance = createConnection();
}
return instance;
},
};
})();
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();
console.log(db1 === db2); // true (same instance)
db1.connect();
/**
* EXAMPLE 9: ES6 Class Singleton
* Singleton using ES6 class syntax
*/
console.log('\n--- Example 9: Class Singleton ---');
class Logger {
static instance = null;
constructor() {
if (Logger.instance) {
return Logger.instance;
}
this.logs = [];
this.level = 'info';
Logger.instance = this;
}
setLevel(level) {
this.level = level;
return this;
}
log(message, level = 'info') {
const entry = {
timestamp: new Date().toISOString(),
level,
message,
};
this.logs.push(entry);
console.log(`[${entry.timestamp}] [${level.toUpperCase()}]: ${message}`);
}
getLogs() {
return [...this.logs];
}
}
const logger1 = new Logger();
const logger2 = new Logger();
console.log(logger1 === logger2); // true
logger1.log('Application started');
/**
* EXAMPLE 10: Mixin Pattern with Objects
* Combine behaviors from multiple sources
*/
console.log('\n--- Example 10: Object Mixins ---');
const Serializable = {
toJSON() {
return JSON.stringify(this);
},
fromJSON(json) {
return Object.assign(this, JSON.parse(json));
},
};
const Comparable = {
equals(other) {
return JSON.stringify(this) === JSON.stringify(other);
},
};
const Timestamped = {
touch() {
this.updatedAt = new Date();
return this;
},
getAge() {
if (!this.createdAt) return 0;
return Date.now() - this.createdAt.getTime();
},
};
function Record(data) {
Object.assign(this, data);
this.createdAt = new Date();
}
// Apply mixins
Object.assign(Record.prototype, Serializable, Comparable, Timestamped);
const record = new Record({ name: 'Test', value: 42 });
record.touch();
console.log(record.toJSON());
/**
* EXAMPLE 11: Functional Mixins
* Mixins as functions that enhance objects
*/
console.log('\n--- Example 11: Functional Mixins ---');
const withLogging = (obj) => ({
...obj,
log(...args) {
console.log(`[${obj.name}]`, ...args);
},
error(...args) {
console.error(`[${obj.name}] ERROR:`, ...args);
},
});
const withEvents = (obj) => {
const events = {};
return {
...obj,
on(event, callback) {
if (!events[event]) events[event] = [];
events[event].push(callback);
return this;
},
emit(event, ...args) {
if (events[event]) {
events[event].forEach((cb) => cb(...args));
}
return this;
},
};
};
const withState = (initialState) => (obj) => {
let state = { ...initialState };
return {
...obj,
getState() {
return { ...state };
},
setState(updates) {
state = { ...state, ...updates };
return this;
},
};
};
// Compose mixins
const compose =
(...fns) =>
(x) =>
fns.reduce((acc, fn) => fn(acc), x);
const createService = compose(
withLogging,
withEvents,
withState({ count: 0, active: false })
);
const service = createService({ name: 'DataService' });
service.log('Service created');
service.setState({ active: true });
console.log(service.getState());
/**
* EXAMPLE 12: Namespace Pattern
* Organize code under single global object
*/
console.log('\n--- Example 12: Namespace Pattern ---');
const MyApp = {};
MyApp.Config = {
version: '1.0.0',
env: 'development',
};
MyApp.Utils = {
format: {
date(d) {
return d.toLocaleDateString();
},
currency(n) {
return `$${n.toFixed(2)}`;
},
},
validators: {
email(str) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
},
phone(str) {
return /^\d{10}$/.test(str);
},
},
};
MyApp.Models = {
User: function (name, email) {
this.name = name;
this.email = email;
},
};
console.log(MyApp.Utils.format.currency(99.5)); // "$99.50"
console.log(MyApp.Utils.validators.email('test@example.com')); // true
/**
* EXAMPLE 13: Builder Pattern
* Step-by-step object construction
*/
console.log('\n--- Example 13: Builder Pattern ---');
class URLBuilder {
constructor() {
this.url = {
protocol: 'https',
host: '',
port: null,
path: [],
query: {},
fragment: null,
};
}
protocol(p) {
this.url.protocol = p;
return this;
}
host(h) {
this.url.host = h;
return this;
}
port(p) {
this.url.port = p;
return this;
}
path(...segments) {
this.url.path.push(...segments);
return this;
}
query(key, value) {
this.url.query[key] = value;
return this;
}
fragment(f) {
this.url.fragment = f;
return this;
}
build() {
let result = `${this.url.protocol}://${this.url.host}`;
if (this.url.port) {
result += `:${this.url.port}`;
}
if (this.url.path.length) {
result += '/' + this.url.path.join('/');
}
const queryString = Object.entries(this.url.query)
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
.join('&');
if (queryString) {
result += `?${queryString}`;
}
if (this.url.fragment) {
result += `#${this.url.fragment}`;
}
return result;
}
}
const url = new URLBuilder()
.host('api.example.com')
.path('v1', 'users')
.query('page', 1)
.query('limit', 10)
.fragment('results')
.build();
console.log(url);
// https://api.example.com/v1/users?page=1&limit=10#results
/**
* EXAMPLE 14: HTML Builder
* Building DOM-like structures
*/
console.log('\n--- Example 14: HTML Builder ---');
class HTMLBuilder {
constructor(tag) {
this.element = {
tag,
attributes: {},
children: [],
text: '',
};
}
attr(key, value) {
this.element.attributes[key] = value;
return this;
}
text(content) {
this.element.text = content;
return this;
}
child(builder) {
this.element.children.push(builder);
return this;
}
build(indent = 0) {
const pad = ' '.repeat(indent);
const { tag, attributes, children, text } = this.element;
let attrs = Object.entries(attributes)
.map(([k, v]) => `${k}="${v}"`)
.join(' ');
if (attrs) attrs = ' ' + attrs;
if (children.length === 0 && !text) {
return `${pad}<${tag}${attrs} />`;
}
let html = `${pad}<${tag}${attrs}>`;
if (text) {
html += text;
}
if (children.length) {
html += '\n';
for (const child of children) {
html += child.build(indent + 1) + '\n';
}
html += pad;
}
html += `</${tag}>`;
return html;
}
}
const el = (tag) => new HTMLBuilder(tag);
const html = el('div')
.attr('class', 'container')
.child(el('h1').text('Welcome'))
.child(
el('ul')
.attr('class', 'list')
.child(el('li').text('Item 1'))
.child(el('li').text('Item 2'))
)
.build();
console.log(html);
/**
* EXAMPLE 15: Observer Pattern (Event Emitter)
* Publish-subscribe mechanism
*/
console.log('\n--- Example 15: Observer Pattern ---');
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;
}
off(event, callback) {
if (this.events.has(event)) {
const callbacks = this.events.get(event);
const index = callbacks.indexOf(callback);
if (index > -1) callbacks.splice(index, 1);
}
return this;
}
once(event, callback) {
const wrapper = (...args) => {
callback(...args);
this.off(event, wrapper);
};
return this.on(event, wrapper);
}
emit(event, ...args) {
if (this.events.has(event)) {
for (const callback of this.events.get(event)) {
callback(...args);
}
}
return this;
}
listenerCount(event) {
return this.events.has(event) ? this.events.get(event).length : 0;
}
}
const emitter = new EventEmitter();
emitter.on('data', (data) => console.log('Received:', data));
emitter.once('connect', () => console.log('Connected!'));
emitter.emit('connect'); // "Connected!"
emitter.emit('connect'); // (nothing - was once)
emitter.emit('data', { value: 42 }); // "Received: { value: 42 }"
/**
* EXAMPLE 16: Observable State
* Reactive state with observers
*/
console.log('\n--- Example 16: Observable State ---');
function createStore(initialState) {
let state = { ...initialState };
const listeners = new Set();
return {
getState() {
return { ...state };
},
setState(updates) {
const prevState = state;
state = { ...state, ...updates };
// Notify all listeners
for (const listener of listeners) {
listener(state, prevState);
}
},
subscribe(listener) {
listeners.add(listener);
// Return unsubscribe function
return () => {
listeners.delete(listener);
};
},
};
}
const store = createStore({ count: 0, name: 'App' });
const unsubscribe = store.subscribe((newState, prevState) => {
console.log('State changed:', prevState, '->', newState);
});
store.setState({ count: 1 });
store.setState({ count: 2 });
unsubscribe();
store.setState({ count: 3 }); // No output
/**
* EXAMPLE 17: Proxy-based Observable
* Automatic change detection with Proxy
*/
console.log('\n--- Example 17: Proxy Observable ---');
function observable(target, onChange) {
return new Proxy(target, {
get(obj, prop) {
const value = obj[prop];
if (typeof value === 'object' && value !== null) {
return observable(value, onChange);
}
return value;
},
set(obj, prop, value) {
const oldValue = obj[prop];
obj[prop] = value;
onChange(prop, value, oldValue);
return true;
},
});
}
const data = observable(
{ user: { name: 'Alice', age: 30 }, active: true },
(prop, newVal, oldVal) => {
console.log(`Changed ${prop}: ${oldVal} -> ${newVal}`);
}
);
data.active = false; // "Changed active: true -> false"
data.user.name = 'Bob'; // "Changed name: Alice -> Bob"
/**
* EXAMPLE 18: Command Pattern
* Encapsulate operations as objects
*/
console.log('\n--- Example 18: Command Pattern ---');
class CommandManager {
constructor() {
this.history = [];
this.undoStack = [];
}
execute(command) {
command.execute();
this.history.push(command);
this.undoStack = []; // Clear redo stack
return this;
}
undo() {
const command = this.history.pop();
if (command) {
command.undo();
this.undoStack.push(command);
}
return this;
}
redo() {
const command = this.undoStack.pop();
if (command) {
command.execute();
this.history.push(command);
}
return this;
}
}
// Example commands
const createAddCommand = (receiver, value) => ({
execute() {
receiver.value += value;
console.log(`Added ${value}, result: ${receiver.value}`);
},
undo() {
receiver.value -= value;
console.log(`Undid add ${value}, result: ${receiver.value}`);
},
});
const manager = new CommandManager();
const calc = { value: 0 };
manager.execute(createAddCommand(calc, 5));
manager.execute(createAddCommand(calc, 10));
console.log(`Current value: ${calc.value}`); // 15
manager.undo(); // Undid add 10
console.log(`After undo: ${calc.value}`); // 5
manager.redo(); // Added 10
console.log(`After redo: ${calc.value}`); // 15
/**
* EXAMPLE 19: Strategy Pattern
* Interchangeable algorithms
*/
console.log('\n--- Example 19: Strategy Pattern ---');
const sortStrategies = {
bubble: (arr) => {
const result = [...arr];
for (let i = 0; i < result.length; i++) {
for (let j = 0; j < result.length - i - 1; j++) {
if (result[j] > result[j + 1]) {
[result[j], result[j + 1]] = [result[j + 1], result[j]];
}
}
}
return result;
},
quick: (arr) => {
if (arr.length <= 1) return arr;
const pivot = arr[Math.floor(arr.length / 2)];
const left = arr.filter((x) => x < pivot);
const middle = arr.filter((x) => x === pivot);
const right = arr.filter((x) => x > pivot);
return [
...sortStrategies.quick(left),
...middle,
...sortStrategies.quick(right),
];
},
native: (arr) => [...arr].sort((a, b) => a - b),
};
class Sorter {
constructor(strategy = 'native') {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
return this;
}
sort(arr) {
console.log(`Sorting with ${this.strategy} strategy`);
return sortStrategies[this.strategy](arr);
}
}
const sorter = new Sorter();
const numbers = [64, 34, 25, 12, 22, 11, 90];
console.log(sorter.sort(numbers));
console.log(sorter.setStrategy('quick').sort(numbers));
/**
* EXAMPLE 20: Complete Application Pattern
* Combining multiple patterns
*/
console.log('\n--- Example 20: Combined Patterns Application ---');
// Application using multiple patterns
const TodoApp = (function () {
// Private state (Module pattern)
const todos = [];
const events = new EventEmitter(); // Observer pattern
// Private ID generator
let nextId = 1;
// Factory pattern for todo creation
function createTodo(title, options = {}) {
return {
id: nextId++,
title,
completed: options.completed || false,
priority: options.priority || 'normal',
createdAt: new Date(),
};
}
// Command pattern for undo/redo
const commandHistory = [];
const undoStack = [];
function executeCommand(command) {
command.execute();
commandHistory.push(command);
undoStack.length = 0;
}
// Public API (Revealing Module)
return {
add(title, options) {
const todo = createTodo(title, options);
const command = {
execute() {
todos.push(todo);
events.emit('add', todo);
},
undo() {
const index = todos.indexOf(todo);
if (index > -1) todos.splice(index, 1);
events.emit('remove', todo);
},
};
executeCommand(command);
return todo;
},
complete(id) {
const todo = todos.find((t) => t.id === id);
if (todo) {
const wasCompleted = todo.completed;
const command = {
execute() {
todo.completed = true;
events.emit('complete', todo);
},
undo() {
todo.completed = wasCompleted;
events.emit('update', todo);
},
};
executeCommand(command);
}
return todo;
},
undo() {
const command = commandHistory.pop();
if (command) {
command.undo();
undoStack.push(command);
}
},
redo() {
const command = undoStack.pop();
if (command) {
command.execute();
commandHistory.push(command);
}
},
getAll() {
return [...todos];
},
on(event, callback) {
events.on(event, callback);
return this;
},
};
})();
// Usage
TodoApp.on('add', (todo) => console.log(`Added: ${todo.title}`));
TodoApp.on('complete', (todo) => console.log(`Completed: ${todo.title}`));
const todo1 = TodoApp.add('Learn patterns', { priority: 'high' });
TodoApp.add('Practice coding');
TodoApp.complete(todo1.id);
console.log('\nAll todos:', TodoApp.getAll());
TodoApp.undo(); // Undo complete
console.log('\nAfter undo:', TodoApp.getAll());
console.log('\n========================================');
console.log('End of Object Patterns Examples');
console.log('========================================');