【JavaScript】面向对象与设计模式
个人主页: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);
}
// getter
get profile() {
return `${this.name}, ${this.age} years old`;
}
// setter
set 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()); // 5
coffee = new MilkDecorator(coffee);
console.log(coffee.description()); // "Plain coffee with milk"
console.log(coffee.cost()); // 6
coffee = 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 {
@readonly
username() {
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;
}
结语
感谢您的阅读!期待您的一键三连!欢迎指正!