JavaScript 对象(千字长文)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

前言:理解 JavaScript 对象的核心价值

在 JavaScript 开发中,对象(Object)是构建复杂功能的核心工具。无论是前端框架的组件化开发,还是后端服务的数据处理,对象都扮演着“组织者”的角色。它将数据与行为封装在单一结构中,就像一个精心设计的收纳盒,既能分类存储物品,又能提供操作这些物品的工具。

对于编程初学者而言,对象可能显得抽象;但通过循序渐进的学习,可以将其转化为灵活解决问题的利器。本文将从基础概念出发,结合实际案例,深入探讨 JavaScript 对象的创建、属性管理、原型链机制等关键知识点,并提供可直接复用的代码示例。


一、对象的基本概念与创建方法

1.1 什么是 JavaScript 对象?

JavaScript 对象是键值对(Key-Value)的集合,每个键(Key)是字符串类型,值(Value)可以是任意数据类型(数字、函数、其他对象等)。例如:

const person = {
  name: "Alice",
  age: 28,
  greet: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};
  • nameage 是数据属性(Data Properties),存储具体信息;
  • greet 是方法属性(Method Properties),定义了对象的行为。

1.2 对象的创建方式

方法一:字面量语法(Literal Notation)

这是最直接的方式,适合小型对象的快速创建:

const car = {
  brand: "Tesla",
  model: "Model S",
  year: 2023,
  accelerate: function(speed) {
    console.log(`加速到 ${speed} km/h`);
  }
};

方法二:构造函数(Constructor Function)

通过函数模板批量生成对象,适合创建多个同类实例:

function Book(title, author) {
  this.title = title;
  this.author = author;
  this.displayInfo = function() {
    console.log(`${this.title} by ${this.author}`);
  };
}

const book1 = new Book("JavaScript 高级程序设计", "Nicholas C. Zakas");
const book2 = new Book("你不知道的 JavaScript", "Kyle Simpson");

方法三:ES6 Class 语法

ES6 引入的类(Class)语法,使面向对象编程更直观:

class Product {
  constructor(name, price) {
    this.name = name;
    this.price = price;
  }
  calculateDiscount(discountRate) {
    return this.price * (1 - discountRate);
  }
}

const phone = new Product("iPhone 15", 999);
console.log(phone.calculateDiscount(0.1)); // 输出 899.1

二、对象的属性与方法详解

2.1 数据属性与访问器属性

JavaScript 对象的属性分为两种类型:
| 类型 | 描述 | 示例代码 | |---------------|----------------------------------------------------------------------|-----------------------------------| | 数据属性 | 直接存储值,通过键名直接访问或修改 | person.age = 30 | | 访问器属性 | 通过 getset 方法间接操作值,常用于数据验证或计算属性 | obj.setAge(25)obj.age |

示例:使用访问器属性控制年龄的合法性

const user = {
  _age: 18,
  get age() {
    return this._age;
  },
  set age(value) {
    if (value < 0) {
      throw new Error("年龄不能为负数");
    }
    this._age = value;
  }
};

user.age = 25; // 正常赋值
console.log(user.age); // 输出 25
user.age = -5; // 抛出错误

2.2 对象方法的绑定问题

在 JavaScript 中,函数脱离对象后,this 的指向可能发生改变。例如:

const calculator = {
  value: 0,
  add: function(num) {
    this.value += num;
  }
};

const addFive = calculator.add; // 直接赋值函数
calculator.value = 10;
addFive(5); // 报错:this.value 为 undefined

解决方案:使用 bind() 明确绑定上下文,或改用箭头函数:

const addFiveBound = calculator.add.bind(calculator);
addFiveBound(5); // 正确输出 15

三、对象的继承与原型链机制

3.1 原型链(Prototype Chain)的核心思想

JavaScript 的继承是通过原型链实现的。每个对象都有一个 __proto__ 属性(非标准)指向其构造函数的原型对象(prototype)。例如:

function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  return "Animal sound";
};

const dog = new Animal("Buddy");
console.log(dog.speak()); // 输出 "Animal sound"

当访问 dog.speak() 时,若 dog 自身没有该方法,则会沿着原型链向上查找,直到找到 Animal.prototype.speak

3.2 原型链的可视化比喻

可以将原型链想象为一个家族树:

  • 对象 是家族中的成员;
  • 原型 是成员的父辈;
  • 原型链 是从自己到祖先的连续关系链。

例如,dog 的原型是 Animal.prototype,而 Animal.prototype 的原型是 Object.prototype,最终指向 null,形成终止条件。


四、对象与数组的关系

4.1 数组的本质是对象

JavaScript 的数组(Array)是对象的子类,其内部通过索引(0, 1, 2...)存储元素。例如:

const fruits = ["apple", "banana", "orange"];
console.log(fruits[0]); // 输出 "apple"
console.log(fruits.length); // 输出 3

但需要注意:

  • 数组的 length 属性会自动更新,如 fruits[5] = "grape" 会将 length 变为 6;
  • 数组方法(如 push()map())是通过 Array.prototype 继承的。

4.2 对象与数组的转换

将对象转为数组

const scores = { math: 90, english: 85 };
// 转为键值对数组
const entries = Object.entries(scores); // [ ["math", 90], ["english", 85] ]
// 转为值数组
const values = Object.values(scores); // [90, 85]

将数组转为对象

const data = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" }
];
// 转为以 id 为键的对象
const idMap = data.reduce((acc, item) => {
  acc[item.id] = item;
  return acc;
}, {}); // { 1: { ... }, 2: { ... } }

五、对象的拷贝与合并

5.1 深拷贝与浅拷贝的区别

  • 浅拷贝(Shallow Copy):复制对象的引用,修改子对象会同时影响原对象。
  • 深拷贝(Deep Copy):递归复制所有嵌套对象,确保完全独立。

示例:浅拷贝的陷阱

const original = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, original);
shallowCopy.b.c = 3;
console.log(original.b.c); // 输出 3(原对象被修改)

实现深拷贝的方法

function deepCopy(obj) {
  return JSON.parse(JSON.stringify(obj));
}

const deepCopyObj = deepCopy(original);
deepCopyObj.b.c = 4;
console.log(original.b.c); // 仍为 3

5.2 合并对象:Object.assign() 与展开运算符

const defaults = { theme: "light", fontSize: 14 };
const userSettings = { theme: "dark" };

// 方法一:Object.assign()
const mergedOptions1 = Object.assign({}, defaults, userSettings);
// 结果:{ theme: "dark", fontSize:14 }

// 方法二:展开运算符(ES6)
const mergedOptions2 = { ...defaults, ...userSettings };

六、ES6 新增的对象特性

6.1 对象简写语法

在构造函数或方法中,若属性名与变量名相同,可省略键名:

const name = "JavaScript";
const version = "ES6";

const feature = {
  name, // 等价于 name: name
  version, // 等价于 version: version
  isModern() { // 等价于 isModern: function() {}
    return true;
  }
};

6.2 计算属性名

使用方括号 [] 动态定义属性名:

const keyName = "price";
const product = {
  [keyName]: 299,
  ["discountedPrice"]: 269.99
};
console.log(product.price); // 输出 299

6.3 类的静态方法与继承

class MathUtil {
  static sqrt(value) {
    return Math.sqrt(value);
  }
}

// 继承父类
class AdvancedMath extends MathUtil {
  constructor() {
    super();
  }
  cube(value) {
    return value ** 3;
  }
}

console.log(AdvancedMath.sqrt(16)); // 输出 4
const math = new AdvancedMath();
console.log(math.cube(3)); // 输出 27

七、实际应用案例:构建购物车系统

7.1 需求分析

实现一个购物车对象,需支持以下功能:

  • 添加商品
  • 删除商品
  • 计算总价
  • 清空购物车

7.2 代码实现

class ShoppingCart {
  constructor() {
    this.items = [];
  }

  addItem(item) {
    this.items.push(item);
  }

  removeItem(index) {
    this.items.splice(index, 1);
  }

  calculateTotal() {
    return this.items.reduce((sum, item) => sum + item.price, 0);
  }

  clearCart() {
    this.items = [];
  }
}

// 使用示例
const cart = new ShoppingCart();
cart.addItem({ name: "Laptop", price: 1200 });
cart.addItem({ name: "Mouse", price: 25 });
console.log(cart.calculateTotal()); // 输出 1225
cart.removeItem(0);
console.log(cart.items.length); // 输出 1

结论:掌握对象,解锁 JavaScript 的无限可能

通过本文的讲解,我们系统学习了 JavaScript 对象的创建、属性管理、继承机制及实际应用。从基础的键值对结构到复杂的原型链,对象不仅是数据的容器,更是实现复杂功能(如面向对象编程、模块化设计)的核心工具。

对于开发者而言,建议通过以下方式深化理解:

  1. 实践优先:尝试用对象重构现有代码,例如将散落的变量封装为对象;
  2. 探索原型链:通过控制台查看对象的 __proto__ 属性,理解继承关系;
  3. 阅读源码:分析开源框架(如 React、Vue)中对象的使用场景,学习最佳实践。

掌握 JavaScript 对象,你将能够更优雅地组织代码逻辑,构建高效、可维护的系统。下一次面对复杂需求时,不妨让对象成为你最得力的“助手”吧!

最新发布