Skip to content

JavaScript 类和对象

类和对象是面向对象编程(OOP)的核心概念。JavaScript 从 ES6 开始引入了类语法,使得面向对象编程更加直观和易于理解。在本章节中,我们将深入学习 JavaScript 中的类和对象。

什么是面向对象编程

面向对象编程(Object-Oriented Programming,OOP)是一种编程范式,它使用对象来组织代码。OOP 的核心概念包括:

  1. 封装(Encapsulation):将数据和操作数据的方法组合在一起
  2. 继承(Inheritance):子类可以继承父类的属性和方法
  3. 多态(Polymorphism):同一接口可以有不同的实现
  4. 抽象(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); // 7

ES6 类语法

基本类定义

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); // true

Object.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 类和对象的核心要点:

  1. 对象基础:属性和方法的集合,使用字面量或构造函数创建
  2. ES6 类语法:使用 class 关键字定义类,constructor 构造函数
  3. Getter 和 Setter:访问和设置属性的特殊方法
  4. 私有属性:使用 # 前缀定义私有属性和方法
  5. 继承:使用 extends 关键字实现类继承,super 调用父类
  6. 静态属性和方法:属于类本身而不是实例的属性和方法
  7. 原型链:JavaScript 对象继承机制的基础
  8. 对象方法:Object.keys()、Object.values()、Object.entries() 等
  9. 设计模式:工厂模式、单例模式等在类中的应用

掌握类和对象是学习面向对象编程的基础。在下一章节中,我们将学习 JavaScript 的类继承。

本站内容仅供学习和研究使用。