個人主頁:Guiat
歸屬專欄:HTML CSS JavaScript
文章目錄
- 1. JavaScript 中的面向對象編程
- 1.1 對象基礎
- 1.2 構造函數
- 1.3 原型和原型鏈
- 1.4 ES6 類
- 1.5 繼承
- 1.6 封裝
- 2. 創建型設計模式
- 2.1 工廠模式
- 2.2 單例模式
- 2.3 建造者模式
- 2.4 原型模式
- 3. 結構型設計模式
- 3.1 適配器模式
- 3.2 裝飾器模式
- 3.3 代理模式
- 3.4 組合模式
- 4. 行為型設計模式
- 4.1 觀察者模式
- 4.2 策略模式
- 4.3 命令模式
- 4.4 迭代器模式
- 4.5 中介者模式
- 5. 實際應用案例
- 5.1 表單驗證系統
正文
1. JavaScript 中的面向對象編程
1.1 對象基礎
在 JavaScript 中,對象是鍵值對的集合,幾乎所有事物都是對象。
// 對象字面量
const person = {name: 'John',age: 30,greet: function() {return `Hello, my name is ${this.name}`;}
};// 訪問屬性
console.log(person.name); // "John"
console.log(person['age']); // 30// 調用方法
console.log(person.greet()); // "Hello, my name is John"
1.2 構造函數
構造函數用于創建和初始化對象。
function Person(name, age) {this.name = name;this.age = age;this.greet = function() {return `Hello, my name is ${this.name}`;};
}// 使用 new 關鍵字創建實例
const john = new Person('John', 30);
const jane = new Person('Jane', 25);console.log(john.greet()); // "Hello, my name is John"
console.log(jane.greet()); // "Hello, my name is Jane"// 驗證實例類型
console.log(john instanceof Person); // true
1.3 原型和原型鏈
每個 JavaScript 對象都連接到一個原型對象,可以從中繼承屬性和方法。
function Person(name, age) {this.name = name;this.age = age;
}// 在原型上添加方法
Person.prototype.greet = function() {return `Hello, my name is ${this.name}`;
};const john = new Person('John', 30);
const jane = new Person('Jane', 25);console.log(john.greet()); // "Hello, my name is John"
console.log(john.greet === jane.greet); // true - 方法共享// 原型鏈
console.log(john.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
1.4 ES6 類
ES6 引入了類語法,使面向對象編程更直觀。
class Person {constructor(name, age) {this.name = name;this.age = age;}// 實例方法greet() {return `Hello, my name is ${this.name}`;}// 靜態方法static createAnonymous() {return new Person('Anonymous', 0);}// getterget profile() {return `${this.name}, ${this.age} years old`;}// setterset profile(value) {[this.name, this.age] = value.split(',');this.age = parseInt(this.age, 10);}
}const john = new Person('John', 30);
console.log(john.greet()); // "Hello, my name is John"// 靜態方法調用
const anonymous = Person.createAnonymous();
console.log(anonymous.name); // "Anonymous"
1.5 繼承
繼承允許一個類基于另一個類創建,共享和擴展其功能。
// ES5 繼承
function Person(name, age) {this.name = name;this.age = age;
}Person.prototype.greet = function() {return `Hello, my name is ${this.name}`;
};function Employee(name, age, company) {// 調用父類構造函數Person.call(this, name, age);this.company = company;
}// 設置原型鏈
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;// 添加新方法
Employee.prototype.work = function() {return `${this.name} works at ${this.company}`;
};// ES6 繼承
class Person {constructor(name, age) {this.name = name;this.age = age;}greet() {return `Hello, my name is ${this.name}`;}
}class Employee extends Person {constructor(name, age, company) {super(name, age); // 調用父類構造函數this.company = company;}work() {return `${this.name} works at ${this.company}`;}// 覆蓋父類方法greet() {return `${super.greet()} and I work at ${this.company}`;}
}const jane = new Employee('Jane', 25, 'Facebook');
console.log(jane.greet()); // "Hello, my name is Jane and I work at Facebook"
1.6 封裝
封裝是隱藏對象內部狀態和實現細節的技術。
// 使用閉包實現私有變量
function Person(name, age) {// 私有變量let _name = name;let _age = age;// 公共接口this.getName = function() {return _name;};this.setName = function(name) {if (name.length > 0) {_name = name;}};
}// ES2022 私有字段和方法
class Person {// 私有字段#name;#age;constructor(name, age) {this.#name = name;this.#age = age;}// 公共方法getName() {return this.#name;}// 私有方法#validateAge(age) {return age > 0 && age < 120;}
}
2. 創建型設計模式
創建型設計模式關注對象的創建機制,以適合特定情況的方式創建對象。
2.1 工廠模式
工廠模式通過一個中央函數創建對象。
// 簡單工廠
function createUser(type) {if (type === 'admin') {return {name: 'Admin User',permissions: ['read', 'write', 'delete']};} else if (type === 'user') {return {name: 'Regular User',permissions: ['read']};}
}const adminUser = createUser('admin');
console.log(adminUser.permissions); // ["read", "write", "delete"]// 工廠方法
class UserFactory {static createAdmin(name) {return {name,permissions: ['read', 'write', 'delete'],role: 'admin'};}static createRegular(name) {return {name,permissions: ['read'],role: 'user'};}
}const admin = UserFactory.createAdmin('John');
const user = UserFactory.createRegular('Jane');
2.2 單例模式
單例模式確保一個類只有一個實例,并提供一個全局訪問點。
// 簡單單例
const Singleton = (function() {let instance;function createInstance() {return {name: 'Singleton Instance',getData: function() {return this.name;}};}return {getInstance: function() {if (!instance) {instance = createInstance();}return instance;}};
})();const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true// ES6 單例
class Database {constructor(host, port) {if (Database.instance) {return Database.instance;}this.host = host;this.port = port;this.connected = false;Database.instance = this;}connect() {if (this.connected) {return this;}console.log(`Connecting to ${this.host}:${this.port}`);this.connected = true;return this;}
}const db1 = new Database('localhost', 3306).connect();
const db2 = new Database('example.com', 8080).connect();
console.log(db1 === db2); // true
console.log(db2.host); // "localhost" (不是 "example.com")
2.3 建造者模式
建造者模式將復雜對象的構建與其表示分離,使同一構建過程可創建不同表示。
// 建造者模式
class HouseBuilder {constructor() {this.house = {};}setBuildingType(buildingType) {this.house.buildingType = buildingType;return this;}setWallMaterial(wallMaterial) {this.house.wallMaterial = wallMaterial;return this;}setNumberOfDoors(number) {this.house.doors = number;return this;}setNumberOfWindows(number) {this.house.windows = number;return this;}build() {return this.house;}
}const house = new HouseBuilder().setBuildingType('apartment').setWallMaterial('brick').setNumberOfDoors(2).setNumberOfWindows(4).build();// 使用指揮者
class HouseDirector {buildCottage(builder) {return builder.setBuildingType('cottage').setWallMaterial('wood').setNumberOfDoors(2).setNumberOfWindows(8).build();}buildApartment(builder) {return builder.setBuildingType('apartment').setWallMaterial('concrete').setNumberOfDoors(1).setNumberOfWindows(4).build();}
}const director = new HouseDirector();
const cottage = director.buildCottage(new HouseBuilder());
2.4 原型模式
原型模式基于現有對象創建新對象,而不是從頭構建。
// ES5 方式
const carPrototype = {init: function(make, model, year) {this.make = make;this.model = model;this.year = year;return this;},getInfo: function() {return `${this.make} ${this.model} (${this.year})`;}
};const car1 = Object.create(carPrototype).init('Toyota', 'Camry', 2020);
const car2 = Object.create(carPrototype).init('Honda', 'Accord', 2022);// 使用類
class Car {constructor(make, model, year) {this.make = make;this.model = model;this.year = year;}getInfo() {return `${this.make} ${this.model} (${this.year})`;}clone() {return new Car(this.make, this.model, this.year);}
}const tesla = new Car('Tesla', 'Model S', 2020);
const clonedTesla = tesla.clone();
clonedTesla.year = 2021;
3. 結構型設計模式
結構型設計模式關注類和對象的組合,形成更大的結構。
3.1 適配器模式
適配器模式使接口不兼容的類能一起工作。
// 舊接口
class OldCalculator {constructor() {this.operations = function(term1, term2, operation) {switch(operation) {case 'add':return term1 + term2;case 'sub':return term1 - term2;default:return NaN;}};}
}// 新接口
class NewCalculator {constructor() {this.add = function(term1, term2) {return term1 + term2;};this.sub = function(term1, term2) {return term1 - term2;};}
}// 適配器
class CalculatorAdapter {constructor() {const newCalc = new NewCalculator();this.operations = function(term1, term2, operation) {switch(operation) {case 'add':return newCalc.add(term1, term2);case 'sub':return newCalc.sub(term1, term2);default:return NaN;}};}
}// 客戶端代碼使用舊接口
const oldCalc = new OldCalculator();
console.log(oldCalc.operations(10, 5, 'add')); // 15// 適配新接口
const adaptedCalc = new CalculatorAdapter();
console.log(adaptedCalc.operations(10, 5, 'add')); // 15
3.2 裝飾器模式
裝飾器模式動態地向對象添加新功能,而不改變其原有結構。
// 基礎組件
class Coffee {cost() {return 5;}description() {return 'Plain coffee';}
}// 裝飾器
class MilkDecorator {constructor(coffee) {this.coffee = coffee;}cost() {return this.coffee.cost() + 1;}description() {return `${this.coffee.description()} with milk`;}
}class SugarDecorator {constructor(coffee) {this.coffee = coffee;}cost() {return this.coffee.cost() + 0.5;}description() {return `${this.coffee.description()} with sugar`;}
}// 使用
let coffee = new Coffee();
console.log(coffee.description()); // "Plain coffee"
console.log(coffee.cost()); // 5coffee = new MilkDecorator(coffee);
console.log(coffee.description()); // "Plain coffee with milk"
console.log(coffee.cost()); // 6coffee = new SugarDecorator(coffee);
console.log(coffee.description()); // "Plain coffee with milk with sugar"
console.log(coffee.cost()); // 6.5// JavaScript 裝飾器(提案)
function readonly(target, name, descriptor) {descriptor.writable = false;return descriptor;
}class User {@readonlyusername() {return 'default';}
}
3.3 代理模式
代理模式為另一個對象提供替代或占位符,以控制對原始對象的訪問。
// 真實主題
class RealImage {constructor(filename) {this.filename = filename;this.loadFromDisk();}loadFromDisk() {console.log(`Loading ${this.filename} from disk`);}display() {console.log(`Displaying ${this.filename}`);}
}// 代理
class ProxyImage {constructor(filename) {this.filename = filename;this.realImage = null;}display() {if (!this.realImage) {this.realImage = new RealImage(this.filename);}this.realImage.display();}
}// 客戶端
const image = new ProxyImage('high-res-photo.jpg');
// 沒有加載圖像
console.log('Image object created');// 加載并顯示圖像
image.display();// 再次顯示(不會重新加載)
image.display();
3.4 組合模式
組合模式將對象組合成樹狀結構,表示"部分-整體"層次結構。
// 組件接口
class UIComponent {constructor(name) {this.name = name;}render() {throw new Error('Render method must be implemented');}getComponentSize() {throw new Error('getComponentSize method must be implemented');}
}// 葉子組件
class Button extends UIComponent {constructor(name) {super(name);}render() {console.log(`Rendering Button: ${this.name}`);}getComponentSize() {return 1;}
}class Input extends UIComponent {constructor(name) {super(name);}render() {console.log(`Rendering Input: ${this.name}`);}getComponentSize() {return 1;}
}// 容器組件
class Form extends UIComponent {constructor(name) {super(name);this.components = [];}add(component) {this.components.push(component);}remove(component) {const index = this.components.indexOf(component);if (index !== -1) {this.components.splice(index, 1);}}render() {console.log(`Rendering Form: ${this.name}`);this.components.forEach(component => component.render());}getComponentSize() {return this.components.reduce((size, component) => {return size + component.getComponentSize();}, 0);}
}// 客戶端代碼
const loginForm = new Form('Login Form');
loginForm.add(new Input('Username'));
loginForm.add(new Input('Password'));
loginForm.add(new Button('Submit'));const registrationForm = new Form('Registration Form');
registrationForm.add(new Input('Name'));
registrationForm.add(new Input('Email'));
registrationForm.add(new Button('Register'));const mainForm = new Form('Main Form');
mainForm.add(loginForm);
mainForm.add(registrationForm);// 渲染整個 UI 樹
mainForm.render();
console.log(`Total components: ${mainForm.getComponentSize()}`);
4. 行為型設計模式
行為型設計模式關注對象之間的通信和職責分配。
4.1 觀察者模式
觀察者模式定義對象間的一對多依賴關系,使一個對象改變時,所有依賴它的對象都得到通知。
// 主題
class Subject {constructor() {this.observers = [];}subscribe(observer) {this.observers.push(observer);}unsubscribe(observer) {this.observers = this.observers.filter(obs => obs !== observer);}notify(data) {this.observers.forEach(observer => observer.update(data));}
}// 觀察者
class Observer {constructor(name) {this.name = name;}update(data) {console.log(`${this.name} received: ${data}`);}
}// 使用
const subject = new Subject();const observer1 = new Observer('Observer 1');
const observer2 = new Observer('Observer 2');subject.subscribe(observer1);
subject.subscribe(observer2);subject.notify('Hello World!');
// "Observer 1 received: Hello World!"
// "Observer 2 received: Hello World!"subject.unsubscribe(observer1);
subject.notify('Hello Again!');
// "Observer 2 received: Hello Again!"
4.2 策略模式
策略模式定義了一系列算法,將每個算法封裝起來,使它們可以互換使用。
// 策略接口
class PaymentStrategy {pay(amount) {throw new Error('pay method must be implemented');}
}// 具體策略
class CreditCardStrategy extends PaymentStrategy {constructor(cardNumber, name, cvv, expiryDate) {super();this.cardNumber = cardNumber;this.name = name;this.cvv = cvv;this.expiryDate = expiryDate;}pay(amount) {console.log(`Paying ${amount} using Credit Card`);// 處理信用卡支付邏輯}
}class PayPalStrategy extends PaymentStrategy {constructor(email, password) {super();this.email = email;this.password = password;}pay(amount) {console.log(`Paying ${amount} using PayPal`);// 處理 PayPal 支付邏輯}
}// 上下文
class ShoppingCart {constructor() {this.items = [];}addItem(item) {this.items.push(item);}calculateTotal() {return this.items.reduce((total, item) => total + item.price, 0);}checkout(paymentStrategy) {const amount = this.calculateTotal();paymentStrategy.pay(amount);}
}// 客戶端代碼
const cart = new ShoppingCart();
cart.addItem({ name: 'Item 1', price: 100 });
cart.addItem({ name: 'Item 2', price: 50 });// 使用信用卡支付
cart.checkout(new CreditCardStrategy('1234-5678-9012-3456', 'John Doe', '123', '12/25'));// 使用 PayPal 支付
cart.checkout(new PayPalStrategy('john@example.com', 'password'));// 使用 JavaScript 函數作為策略
const paymentStrategies = {creditCard: function(amount) {console.log(`Paying ${amount} using Credit Card`);},paypal: function(amount) {console.log(`Paying ${amount} using PayPal`);},bitcoin: function(amount) {console.log(`Paying ${amount} using Bitcoin`);}
};function processPayment(amount, strategy) {return paymentStrategies[strategy](amount);
}processPayment(150, 'creditCard'); // "Paying 150 using Credit Card"
processPayment(150, 'paypal'); // "Paying 150 using PayPal"
4.3 命令模式
命令模式將請求封裝為對象,使用者和發送者解耦。
// 命令接口
class Command {execute() {throw new Error('execute method must be implemented');}undo() {throw new Error('undo method must be implemented');}
}// 接收者
class Light {constructor() {this.isOn = false;}turnOn() {this.isOn = true;console.log('Light is now ON');}turnOff() {this.isOn = false;console.log('Light is now OFF');}
}// 具體命令
class TurnOnCommand extends Command {constructor(light) {super();this.light = light;}execute() {this.light.turnOn();}undo() {this.light.turnOff();}
}class TurnOffCommand extends Command {constructor(light) {super();this.light = light;}execute() {this.light.turnOff();}undo() {this.light.turnOn();}
}// 調用者
class RemoteControl {constructor() {this.commands = [];this.history = [];}setCommand(slot, command) {this.commands[slot] = command;}pressButton(slot) {const command = this.commands[slot];if (command) {command.execute();this.history.push(command);}}pressUndoButton() {const command = this.history.pop();if (command) {command.undo();}}
}// 客戶端代碼
const light = new Light();
const turnOn = new TurnOnCommand(light);
const turnOff = new TurnOffCommand(light);const remote = new RemoteControl();
remote.setCommand(0, turnOn);
remote.setCommand(1, turnOff);remote.pressButton(0); // "Light is now ON"
remote.pressButton(1); // "Light is now OFF"
remote.pressUndoButton(); // "Light is now ON"
4.4 迭代器模式
迭代器模式提供一種順序訪問集合元素的方法,而不暴露內部表示。
// 自定義迭代器
class ArrayIterator {constructor(array) {this.array = array;this.index = 0;}hasNext() {return this.index < this.array.length;}next() {return this.hasNext() ? this.array[this.index++] : null;}
}// 可迭代集合
class Collection {constructor() {this.items = [];}addItem(item) {this.items.push(item);}getIterator() {return new ArrayIterator(this.items);}
}// 使用迭代器
const collection = new Collection();
collection.addItem('Item 1');
collection.addItem('Item 2');
collection.addItem('Item 3');const iterator = collection.getIterator();
while (iterator.hasNext()) {console.log(iterator.next());
}// ES6 實現迭代器協議和可迭代協議
class NumberRange {constructor(start, end) {this.start = start;this.end = end;}// 使對象可迭代[Symbol.iterator]() {let current = this.start;const end = this.end;// 迭代器對象return {next() {if (current <= end) {return { value: current++, done: false };} else {return { done: true };}}};}
}// 使用 for...of 遍歷
for (const num of new NumberRange(1, 5)) {console.log(num); // 1, 2, 3, 4, 5
}
4.5 中介者模式
中介者模式通過引入中介對象來減少對象之間的直接通信,降低耦合度。
// 中介者
class ChatRoom {constructor() {this.users = {};}register(user) {this.users[user.name] = user;user.chatRoom = this;}send(message, from, to) {if (to) {// 私聊消息this.users[to].receive(message, from);} else {// 廣播消息for (const key in this.users) {if (this.users[key] !== from) {this.users[key].receive(message, from);}}}}
}// 同事類
class User {constructor(name) {this.name = name;this.chatRoom = null;}send(message, to) {this.chatRoom.send(message, this, to);}receive(message, from) {console.log(`${from.name} to ${this.name}: ${message}`);}
}// 使用
const chatRoom = new ChatRoom();const john = new User('John');
const jane = new User('Jane');
const bob = new User('Bob');chatRoom.register(john);
chatRoom.register(jane);
chatRoom.register(bob);john.send('Hi everyone!');
jane.send('Hey John!', 'John');
bob.send('Hello!');
5. 實際應用案例
5.1 表單驗證系統
結合了策略模式和裝飾器模式。
// 驗證策略
const validators = {required: (value) => value.trim() !== '' ? null : 'This field is required',minLength: (value, min) => value.length >= min ? null : `Must be at least ${min} characters`,maxLength: (value, max) => value.length <= max ? null : `Cannot exceed ${max} characters`,email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ? null : 'Invalid email format',pattern: (value, regex) => regex.test(value) ? null : 'Invalid format'
};// 表單驗證器類
class FormValidator {constructor() {this.validations = {};}// 添加驗證規則addValidation(field, validations) {this.validations[field] = validations;}
結語
感謝您的閱讀!期待您的一鍵三連!歡迎指正!