JavaScript 类和对象
类和对象是面向对象编程(OOP)的核心概念。JavaScript 从 ES6 开始引入了类语法,使得面向对象编程更加直观和易于理解。在本章节中,我们将深入学习 JavaScript 中的类和对象。
什么是面向对象编程
面向对象编程(Object-Oriented Programming,OOP)是一种编程范式,它使用对象来组织代码。OOP 的核心概念包括:
- 封装(Encapsulation):将数据和操作数据的方法组合在一起
- 继承(Inheritance):子类可以继承父类的属性和方法
- 多态(Polymorphism):同一接口可以有不同的实现
- 抽象(Abstraction):隐藏复杂的实现细节,只暴露必要的接口
对象基础
对象字面量
在 JavaScript 中,对象是属性和方法的集合:
javascript
// 对象字面量
const person = {
name: "张三",
age: 25,
greet: function() {
return `你好,我是${this.name}`;
}
};
console.log(person.name); // "张三"
console.log(person.greet()); // "你好,我是张三"对象属性访问
javascript
const user = {
name: "李四",
"full-name": "李四丰",
age: 30
};
// 点表示法
console.log(user.name); // "李四"
// 方括号表示法
console.log(user["full-name"]); // "李四丰"
console.log(user["age"]); // 30
// 动态属性访问
const propertyName = "name";
console.log(user[propertyName]); // "李四"对象方法
javascript
const calculator = {
result: 0,
add: function(num) {
this.result += num;
return this;
},
subtract: function(num) {
this.result -= num;
return this;
},
getResult: function() {
return this.result;
},
reset: function() {
this.result = 0;
return this;
}
};
// 链式调用
const result = calculator.add(10).subtract(3).getResult();
console.log(result); // 7ES6 类语法
基本类定义
javascript
// 定义一个简单的类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `你好,我是${this.name},今年${this.age}岁`;
}
// 静态方法
static getSpecies() {
return "人类";
}
}
// 创建类的实例
const person1 = new Person("张三", 25);
const person2 = new Person("李四", 30);
console.log(person1.greet()); // "你好,我是张三,今年25岁"
console.log(Person.getSpecies()); // "人类"构造函数
javascript
class Rectangle {
constructor(width, height) {
// 参数验证
if (width <= 0 || height <= 0) {
throw new Error("宽度和高度必须大于0");
}
this.width = width;
this.height = height;
}
// 计算面积
getArea() {
return this.width * this.height;
}
// 计算周长
getPerimeter() {
return 2 * (this.width + this.height);
}
// 获取描述信息
getDescription() {
return `这是一个${this.width}x${this.height}的矩形`;
}
}
const rect = new Rectangle(10, 5);
console.log(rect.getArea()); // 50
console.log(rect.getPerimeter()); // 30
console.log(rect.getDescription()); // "这是一个10x5的矩形"Getter 和 Setter
javascript
class User {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this._age = 0; // 私有属性约定
}
// Getter
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
get age() {
return this._age;
}
// Setter
set age(value) {
if (value < 0) {
throw new Error("年龄不能为负数");
}
this._age = value;
}
// 只读属性
get species() {
return "人类";
}
}
const user = new User("张", "三");
console.log(user.fullName); // "张 三"
user.age = 25;
console.log(user.age); // 25
console.log(user.species); // "人类"
// user.species = "外星人"; // 错误:Cannot set property species私有属性和方法(ES2022)
javascript
class BankAccount {
// 私有属性
#balance = 0;
#accountNumber;
constructor(accountNumber, initialBalance = 0) {
this.#accountNumber = accountNumber;
this.#balance = initialBalance;
}
// 公共方法
deposit(amount) {
if (amount <= 0) {
throw new Error("存款金额必须大于0");
}
this.#balance += amount;
return this.#balance;
}
withdraw(amount) {
if (amount <= 0) {
throw new Error("取款金额必须大于0");
}
if (amount > this.#balance) {
throw new Error("余额不足");
}
this.#balance -= amount;
return this.#balance;
}
getBalance() {
return this.#balance;
}
// 私有方法
#validateTransaction(amount) {
return amount > 0 && amount <= this.#balance;
}
// 公共方法可以调用私有方法
transfer(amount, targetAccount) {
if (this.#validateTransaction(amount)) {
this.withdraw(amount);
targetAccount.deposit(amount);
return true;
}
return false;
}
}
const account1 = new BankAccount("123456", 1000);
const account2 = new BankAccount("789012", 500);
account1.deposit(200);
console.log(account1.getBalance()); // 1200
account1.withdraw(100);
console.log(account1.getBalance()); // 1100
// console.log(account1.#balance); // 错误:Private field '#balance' must be declared in an enclosing class类的继承
基本继承
javascript
// 父类
class Animal {
constructor(name, species) {
this.name = name;
this.species = species;
}
speak() {
return `${this.name} 发出声音`;
}
getInfo() {
return `我是${this.species},名叫${this.name}`;
}
}
// 子类
class Dog extends Animal {
constructor(name, breed) {
super(name, "狗"); // 调用父类构造函数
this.breed = breed;
}
// 重写父类方法
speak() {
return `${this.name} 汪汪叫`;
}
// 子类特有方法
fetch() {
return `${this.name} 去捡球`;
}
}
class Cat extends Animal {
constructor(name, color) {
super(name, "猫");
this.color = color;
}
speak() {
return `${this.name} 喵喵叫`;
}
climb() {
return `${this.name} 爬树`;
}
}
const dog = new Dog("旺财", "金毛");
const cat = new Cat("咪咪", "橘色");
console.log(dog.speak()); // "旺财 汪汪叫"
console.log(dog.fetch()); // "旺财 去捡球"
console.log(dog.getInfo()); // "我是狗,名叫旺财"
console.log(cat.speak()); // "咪咪 喵喵叫"
console.log(cat.climb()); // "咪咪 爬树"静态属性和方法
javascript
class MathUtils {
// 静态属性(ES2022)
static PI = 3.14159;
// 静态方法
static add(a, b) {
return a + b;
}
static multiply(a, b) {
return a * b;
}
static circleArea(radius) {
return this.PI * radius * radius; // 使用静态属性
}
static isEven(number) {
return number % 2 === 0;
}
}
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.add(5, 3)); // 8
console.log(MathUtils.circleArea(5)); // 78.53975
console.log(MathUtils.isEven(4)); // true类表达式
javascript
// 类表达式
const Rectangle = class {
constructor(width, height) {
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
};
// 命名类表达式
const Square = class SquareClass {
constructor(side) {
this.side = side;
}
getArea() {
return this.side * this.side;
}
// 可以在类内部引用类名
clone() {
return new SquareClass(this.side);
}
};
const rect = new Rectangle(10, 5);
console.log(rect.getArea()); // 50
const square = new Square(4);
console.log(square.getArea()); // 16
const clonedSquare = square.clone();
console.log(clonedSquare.getArea()); // 16对象原型
原型链
javascript
// 构造函数(ES5 方式)
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
return `你好,我是${this.name}`;
};
Person.prototype.getAge = function() {
return this.age;
};
const person = new Person("张三", 25);
console.log(person.greet()); // "你好,我是张三"
console.log(person.getAge()); // 25
// 检查原型链
console.log(person.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // trueObject.create()
javascript
// 使用 Object.create() 创建对象
const animal = {
species: "动物",
speak: function() {
return `${this.name} 发出声音`;
}
};
const dog = Object.create(animal);
dog.name = "旺财";
dog.breed = "金毛";
console.log(dog.speak()); // "旺财 发出声音"
console.log(dog.species); // "动物"
// 创建没有原型的对象
const obj = Object.create(null);
obj.name = "张三";
// console.log(obj.toString); // undefined对象方法
Object.keys(), Object.values(), Object.entries()
javascript
const user = {
name: "张三",
age: 25,
city: "北京"
};
// 获取所有键
console.log(Object.keys(user)); // ["name", "age", "city"]
// 获取所有值
console.log(Object.values(user)); // ["张三", 25, "北京"]
// 获取键值对数组
console.log(Object.entries(user));
// [["name", "张三"], ["age", 25], ["city", "北京"]]
// 遍历对象
Object.entries(user).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});Object.assign()
javascript
// 对象合并
const target = { a: 1, b: 2 };
const source1 = { b: 4, c: 5 };
const source2 = { c: 6, d: 7 };
const result = Object.assign(target, source1, source2);
console.log(result); // { a: 1, b: 4, c: 6, d: 7 }
// 浅拷贝
const original = { name: "张三", age: 25 };
const copy = Object.assign({}, original);
copy.age = 26;
console.log(original.age); // 25
console.log(copy.age); // 26对象解构
javascript
const user = {
name: "张三",
age: 25,
address: {
city: "北京",
district: "朝阳区"
},
hobbies: ["读书", "游泳"]
};
// 基本解构
const { name, age } = user;
console.log(name, age); // "张三" 25
// 重命名
const { name: userName, age: userAge } = user;
console.log(userName, userAge); // "张三" 25
// 默认值
const { name: n, gender = "未知" } = user;
console.log(n, gender); // "张三" "未知"
// 嵌套解构
const { address: { city, district } } = user;
console.log(city, district); // "北京" "朝阳区"
// 数组解构
const { hobbies: [firstHobby, secondHobby] } = user;
console.log(firstHobby, secondHobby); // "读书" "游泳"类和对象的最佳实践
1. 使用类进行复杂对象建模
javascript
class ShoppingCart {
constructor() {
this.items = [];
this.discount = 0;
}
addItem(product, quantity = 1) {
const existingItem = this.items.find(item => item.product.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.items.push({ product, quantity });
}
return this;
}
removeItem(productId) {
this.items = this.items.filter(item => item.product.id !== productId);
return this;
}
updateQuantity(productId, quantity) {
const item = this.items.find(item => item.product.id === productId);
if (item) {
item.quantity = quantity;
}
return this;
}
getTotalPrice() {
const subtotal = this.items.reduce((total, item) => {
return total + (item.product.price * item.quantity);
}, 0);
return subtotal * (1 - this.discount);
}
setDiscount(discount) {
if (discount < 0 || discount > 1) {
throw new Error("折扣必须在0到1之间");
}
this.discount = discount;
return this;
}
getItemsCount() {
return this.items.reduce((count, item) => count + item.quantity, 0);
}
clear() {
this.items = [];
this.discount = 0;
return this;
}
// 转换为 JSON
toJSON() {
return {
items: this.items.map(item => ({
product: item.product,
quantity: item.quantity
})),
discount: this.discount,
total: this.getTotalPrice(),
count: this.getItemsCount()
};
}
}
// 使用示例
const cart = new ShoppingCart();
const product1 = { id: 1, name: "笔记本电脑", price: 5000 };
const product2 = { id: 2, name: "鼠标", price: 100 };
cart.addItem(product1, 1)
.addItem(product2, 2)
.setDiscount(0.1); // 10% 折扣
console.log(cart.getTotalPrice()); // 4680
console.log(cart.getItemsCount()); // 3
console.log(JSON.stringify(cart.toJSON(), null, 2));2. 使用工厂模式创建对象
javascript
class UserFactory {
static createUser(type, name, ...args) {
switch (type) {
case "admin":
return new AdminUser(name, ...args);
case "customer":
return new CustomerUser(name, ...args);
case "guest":
return new GuestUser(name);
default:
throw new Error("未知的用户类型");
}
}
}
class User {
constructor(name) {
this.name = name;
this.createdAt = new Date();
}
getInfo() {
return `用户: ${this.name}, 创建时间: ${this.createdAt}`;
}
}
class AdminUser extends User {
constructor(name, permissions = []) {
super(name);
this.role = "admin";
this.permissions = permissions;
}
getInfo() {
return `${super.getInfo()}, 角色: ${this.role}, 权限: ${this.permissions.join(", ")}`;
}
hasPermission(permission) {
return this.permissions.includes(permission);
}
}
class CustomerUser extends User {
constructor(name, customerId) {
super(name);
this.role = "customer";
this.customerId = customerId;
this.orders = [];
}
addOrder(order) {
this.orders.push(order);
}
getOrderCount() {
return this.orders.length;
}
getInfo() {
return `${super.getInfo()}, 角色: ${this.role}, 订单数: ${this.getOrderCount()}`;
}
}
class GuestUser extends User {
constructor(name) {
super(name || "访客");
this.role = "guest";
this.sessionId = Math.random().toString(36).substr(2, 9);
}
getInfo() {
return `${super.getInfo()}, 角色: ${this.role}, 会话ID: ${this.sessionId}`;
}
}
// 使用工厂创建不同类型的用户
const admin = UserFactory.createUser("admin", "管理员", ["read", "write", "delete"]);
const customer = UserFactory.createUser("customer", "顾客", "CUST001");
const guest = UserFactory.createUser("guest", "访客");
console.log(admin.getInfo());
console.log(customer.getInfo());
console.log(guest.getInfo());3. 使用单例模式
javascript
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance;
}
this.logs = [];
this.level = "info";
Logger.instance = this;
return this;
}
static getInstance() {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
setLevel(level) {
this.level = level;
}
log(message, level = "info") {
if (this.shouldLog(level)) {
const logEntry = {
timestamp: new Date().toISOString(),
level: level,
message: message
};
this.logs.push(logEntry);
console.log(`[${logEntry.timestamp}] [${logEntry.level.toUpperCase()}] ${logEntry.message}`);
}
}
info(message) {
this.log(message, "info");
}
warn(message) {
this.log(message, "warn");
}
error(message) {
this.log(message, "error");
}
shouldLog(level) {
const levels = { debug: 0, info: 1, warn: 2, error: 3 };
return levels[level] >= levels[this.level];
}
getLogs() {
return this.logs;
}
clearLogs() {
this.logs = [];
}
}
// 使用单例
const logger1 = Logger.getInstance();
const logger2 = Logger.getInstance();
console.log(logger1 === logger2); // true
logger1.info("这是第一条日志");
logger2.warn("这是第二条日志");
logger1.error("这是第三条日志");
console.log(logger1.getLogs().length); // 3
console.log(logger2.getLogs().length); // 3实际应用示例
1. 用户管理系统
javascript
class UserManager {
constructor() {
this.users = new Map();
this.nextId = 1;
}
createUser(userData) {
const id = this.nextId++;
const user = new User({
id,
...userData,
createdAt: new Date()
});
this.users.set(id, user);
return user;
}
getUser(id) {
return this.users.get(id);
}
updateUser(id, updates) {
const user = this.users.get(id);
if (user) {
user.update(updates);
return user;
}
return null;
}
deleteUser(id) {
return this.users.delete(id);
}
findUsers(query) {
const results = [];
for (const [id, user] of this.users) {
if (this.matchesQuery(user, query)) {
results.push(user);
}
}
return results;
}
matchesQuery(user, query) {
for (const [key, value] of Object.entries(query)) {
if (user[key] !== value) {
return false;
}
}
return true;
}
getAllUsers() {
return Array.from(this.users.values());
}
getUserCount() {
return this.users.size;
}
}
class User {
constructor(data) {
this.id = data.id;
this.name = data.name;
this.email = data.email;
this.age = data.age;
this.role = data.role || "user";
this.createdAt = data.createdAt;
this.isActive = data.isActive !== undefined ? data.isActive : true;
}
update(updates) {
for (const [key, value] of Object.entries(updates)) {
if (key !== "id" && key !== "createdAt") {
this[key] = value;
}
}
}
deactivate() {
this.isActive = false;
}
activate() {
this.isActive = true;
}
getInfo() {
return {
id: this.id,
name: this.name,
email: this.email,
age: this.age,
role: this.role,
isActive: this.isActive,
createdAt: this.createdAt
};
}
toJSON() {
return this.getInfo();
}
}
// 使用示例
const userManager = new UserManager();
const user1 = userManager.createUser({
name: "张三",
email: "zhangsan@example.com",
age: 25,
role: "admin"
});
const user2 = userManager.createUser({
name: "李四",
email: "lisi@example.com",
age: 30
});
console.log(userManager.getUserCount()); // 2
console.log(userManager.getUser(1).getInfo());
const admins = userManager.findUsers({ role: "admin" });
console.log("管理员数量:", admins.length);
user2.update({ age: 31 });
console.log(userManager.getUser(2).getInfo());2. 事件管理系统
javascript
class EventManager {
constructor() {
this.events = new Map();
this.listeners = new Map();
}
// 创建事件
createEvent(eventData) {
const event = new Event({
id: this.generateId(),
...eventData,
createdAt: new Date()
});
this.events.set(event.id, event);
return event;
}
// 获取事件
getEvent(id) {
return this.events.get(id);
}
// 更新事件
updateEvent(id, updates) {
const event = this.events.get(id);
if (event) {
event.update(updates);
return event;
}
return null;
}
// 删除事件
deleteEvent(id) {
return this.events.delete(id);
}
// 添加监听器
addListener(eventType, callback) {
if (!this.listeners.has(eventType)) {
this.listeners.set(eventType, new Set());
}
this.listeners.get(eventType).add(callback);
}
// 移除监听器
removeListener(eventType, callback) {
if (this.listeners.has(eventType)) {
this.listeners.get(eventType).delete(callback);
}
}
// 触发事件
emit(eventType, data) {
if (this.listeners.has(eventType)) {
for (const callback of this.listeners.get(eventType)) {
try {
callback(data);
} catch (error) {
console.error("事件处理错误:", error);
}
}
}
}
// 搜索事件
searchEvents(criteria) {
const results = [];
for (const [id, event] of this.events) {
if (this.matchesCriteria(event, criteria)) {
results.push(event);
}
}
return results;
}
matchesCriteria(event, criteria) {
for (const [key, value] of Object.entries(criteria)) {
if (typeof value === "function") {
if (!value(event[key])) {
return false;
}
} else if (event[key] !== value) {
return false;
}
}
return true;
}
generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
}
getAllEvents() {
return Array.from(this.events.values());
}
}
class Event {
constructor(data) {
this.id = data.id;
this.title = data.title;
this.description = data.description;
this.startDate = new Date(data.startDate);
this.endDate = new Date(data.endDate);
this.location = data.location;
this.category = data.category || "general";
this.priority = data.priority || "normal";
this.createdAt = data.createdAt;
this.isCompleted = data.isCompleted || false;
}
update(updates) {
for (const [key, value] of Object.entries(updates)) {
if (key !== "id" && key !== "createdAt") {
if (key === "startDate" || key === "endDate") {
this[key] = new Date(value);
} else {
this[key] = value;
}
}
}
}
complete() {
this.isCompleted = true;
}
cancel() {
this.isCompleted = false;
}
isOverdue() {
return !this.isCompleted && this.endDate < new Date();
}
getDuration() {
return this.endDate - this.startDate;
}
getInfo() {
return {
id: this.id,
title: this.title,
description: this.description,
startDate: this.startDate,
endDate: this.endDate,
location: this.location,
category: this.category,
priority: this.priority,
isCompleted: this.isCompleted,
isOverdue: this.isOverdue(),
duration: this.getDuration(),
createdAt: this.createdAt
};
}
toJSON() {
return this.getInfo();
}
}
// 使用示例
const eventManager = new EventManager();
// 添加事件监听器
eventManager.addListener("eventCreated", (event) => {
console.log(`新事件创建: ${event.title}`);
});
eventManager.addListener("eventCompleted", (event) => {
console.log(`事件完成: ${event.title}`);
});
// 创建事件
const meeting = eventManager.createEvent({
title: "团队会议",
description: "项目进度讨论",
startDate: "2024-01-15T10:00:00",
endDate: "2024-01-15T11:00:00",
location: "会议室A",
category: "work",
priority: "high"
});
const birthday = eventManager.createEvent({
title: "生日聚会",
description: "朋友生日庆祝",
startDate: "2024-01-20T18:00:00",
endDate: "2024-01-20T22:00:00",
location: "餐厅",
category: "personal",
priority: "normal"
});
// 触发事件
eventManager.emit("eventCreated", meeting.getInfo());
// 搜索事件
const workEvents = eventManager.searchEvents({ category: "work" });
console.log("工作事件数量:", workEvents.length);
// 标记事件完成
meeting.complete();
eventManager.emit("eventCompleted", meeting.getInfo());
// 检查过期事件
const overdueEvents = eventManager.searchEvents({
isCompleted: false,
isOverdue: (isOverdue) => isOverdue
});
console.log("过期事件数量:", overdueEvents.length);总结
JavaScript 类和对象的核心要点:
- 对象基础:属性和方法的集合,使用字面量或构造函数创建
- ES6 类语法:使用 class 关键字定义类,constructor 构造函数
- Getter 和 Setter:访问和设置属性的特殊方法
- 私有属性:使用 # 前缀定义私有属性和方法
- 继承:使用 extends 关键字实现类继承,super 调用父类
- 静态属性和方法:属于类本身而不是实例的属性和方法
- 原型链:JavaScript 对象继承机制的基础
- 对象方法:Object.keys()、Object.values()、Object.entries() 等
- 设计模式:工厂模式、单例模式等在类中的应用
掌握类和对象是学习面向对象编程的基础。在下一章节中,我们将学习 JavaScript 的类继承。