Skip to content

对象定义

对象是 JavaScript 的基础。理解它们是掌握这门语言的关键。

在 JavaScript 中,对象是键值对的集合。这些对通常被称为属性。

虽然 JavaScript 有原始数据类型(如字符串、数字、布尔值),但几乎所有其他东西都表现得像对象,或者可以通过包装对象暂时视为对象。

  • 原始值(string、number、boolean、null、undefined、symbol、bigint)本身不是对象,但 string、number 和 boolean 原始值有相应的对象包装器(String、Number、Boolean),它们提供方法。
  • 日期始终是对象(Date)。
  • Math 是一个内置的全局对象,具有静态属性和方法(Math)。
  • 正则表达式是对象(RegExp)。
  • 数组是一种特殊类型的对象。
  • 函数是第一等对象。
  • 当然,你创建的自定义对象也是对象。

JavaScript 的原始值是不可变的,而对象是可变的。

一个简单的变量只持有一个值:

let personName = "John Doe"; // 一个字符串原始值

然而,对象可以容纳许多值,这些值通过键(名称)组织起来。这些键值对称为属性。

const person = {
firstName: "John",
lastName: "Doe",
age: 50,
eyeColor: "blue"
};
一个 JavaScript 对象是属性的集合,其中每个属性都有一个名称(键)和一个值。

JavaScript 对象中命名的值称为属性。键通常是字符串(或 Symbols),值可以是任何数据类型,包括其他对象或函数。

对于上面的 person 对象:

属性键属性值
firstName”John”
lastName”Doe”
age50
eyeColor”blue”

这种键值结构类似于:

  • Python 中的字典 (Dictionaries)
  • Java 和 C# 中的哈希映射 (Hash maps) 或哈希表 (Hash tables)
  • PHP 中的关联数组 (Associative arrays)
  • Ruby 和 Perl 中的哈希 (Hashes)

方法是作为对象属性存储的函数。它们定义了可以在对象上或由对象执行的操作。

const personWithMethod = {
firstName: "Jane",
lastName: "Doe",
age: 30,
eyeColor: "green",
fullName: function() {
return this.firstName + " " + this.lastName;
},
// ES6 简写方法语法
greet() {
console.log("Hello, my name is " + this.fullName());
}
};
console.log(personWithMethod.fullName()); // 输出: Jane Doe
personWithMethod.greet(); // 输出: Hello, my name is Jane Doe
JavaScript 对象是属性的动态集合,其中一些属性可以是方法(函数)。

在后续章节中,你将学习更多关于函数和 this 关键字的知识。

在 JavaScript 中,有几种创建对象的方法:

  • 对象字面量 (Object Literal): 最简单、最常见的方式。
  • 构造函数 (Constructor Functions): 创建多个相同类型对象的传统方式。
  • ES6 类 (ES6 Classes): 创建对象和实现继承的现代语法(它是构造函数和原型的语法糖)。
  • 使用 new Object(): 通常不推荐,更倾向于使用对象字面量。
  • 使用 Object.create(): 创建一个具有指定原型对象的新对象。

这是最直接的方法。你可以在一个语句中定义并创建一个对象。

const car = {
make: "Toyota",
model: "Camry",
year: 2021,
startEngine() {
console.log("Engine started!"); // 引擎启动!
}
};

使用构造函数(ES6 之前的风格)

Section titled “使用构造函数(ES6 之前的风格)”

构造函数用于创建多个具有相同结构和行为的对象。

function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
this.describe = function() {
return `${this.firstName} ${this.lastName}, ${this.age} years old.`;
};
}
const myFather = new Person("John", "Doe", 50, "blue");
const myMother = new Person("Sally", "Rally", 48, "green");
console.log(myFather.describe()); // John Doe, 50 岁。

ES6 类为创建对象和处理继承提供了更清晰的语法。它们是 JavaScript 现有基于原型的继承的语法糖。

class Vehicle {
constructor(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
getInfo() {
return `${this.year} ${this.make} ${this.model}`;
}
}
const myCar = new Vehicle("Honda", "Civic", 2022);
console.log(myCar.getInfo()); // 2022 本田 思域
对于简单的、一次性使用的对象,首选对象字面量。对于创建多个相似对象的蓝图,ES6 类是现代标准方法,但了解构造函数对于理解旧代码库或更深层的 JavaScript 概念很有帮助。

在 JavaScript 中,this 指向当前正在执行代码的对象。它的值取决于函数是如何被调用的(调用位置)。

  • 在方法中,this 指向所属对象。
  • 在普通函数中(不是方法,不是箭头函数),在非严格模式下,this 指向全局对象(浏览器中的 window,Node.js 中的 global),在严格模式下则指向 undefined。
  • 在构造函数中(使用 new 调用),this 指向新创建的实例。
  • 在事件处理程序中,this 通常指向接收事件的元素。
  • 箭头函数没有自己的 this 绑定。它们从其周围的(词法)作用域继承 this。

理解 this 至关重要,但也可能很棘手。它的行为是初学者常见的困惑来源。MDN Web Docs 等资源提供了详细解释。

this 是一个关键字,不是变量。它的值不能被重新赋值。

JavaScript 为原生对象提供了内置构造函数,例如 Object、String、Number、Boolean、Array、RegExp、Function、Date。

然而,对于 String、Number、Boolean、Array、Object 和 RegExp,几乎总是最好使用它们的字面量形式,以提高性能和可读性:

// 首选的字面量形式:
const obj = {}; // 代替 new Object()
const str = "hello"; // 代替 new String("hello")
const num = 123; // 代替 new Number(123)
const bool = true; // 代替 new Boolean(true)
const arr = []; // 代替 new Array()
const regex = /pattern/g; // 代替 new RegExp("pattern", "g")
// 函数表达式/声明优于 new Function()
const greet = function() { console.log("Hello!"); };
// Date 是一个例外;你必须使用构造函数
const today = new Date();

Math 对象是一个全局的静态对象;你不需要使用 new 来调用它(例如,Math.random())。

使用像 new String("text") 这样的构造函数会创建一个对象包装器,而不是原始字符串。这可能在比较时导致意外的行为。

JavaScript 中的对象是引用类型。这意味着当你将一个对象赋给一个变量或将其传递给一个函数时,你处理的是内存中该对象的引用(或指针),而不是对象本身的副本。

const originalPerson = {
firstName: "John",
lastName: "Doe",
age: 50
};
const anotherPersonRef = originalPerson; // 两个变量指向同一个对象
anotherPersonRef.age = 55; // 通过一个引用修改会影响原始对象
console.log(originalPerson.age); // 输出: 55 (已改变!)
console.log(anotherPersonRef.age); // 输出: 55

这与原始类型(字符串、数字、布尔值)不同,原始类型是不可变的,并且通过值复制。

要创建对象的真正副本(无论是浅拷贝还是深拷贝),你需要使用展开语法({...obj} 用于浅拷贝)、Object.assign({}, obj)(浅拷贝)或更复杂的方法/库来进行深拷贝。当你想要修改一个对象而不影响原始对象时,这是一个重要的概念。