在 JavaScript 中,对象(Object)是一种复合数据类型,它是属性和方法的集合。属性是键值对(key-value pair),其中键是字符串(或 Symbol),值可以是任何数据类型(数字、字符串、布尔值、数组、甚至是其他对象)。方法是值为函数的属性。
几乎 JavaScript 中的所有东西都是对象,或者可以被当作对象来处理。
创建对象主要有两种方式:对象字面量和 new Object() 构造函数。
这是最常用、最简洁的方式。
// 创建一个空对象const person = {};
// 创建一个有属性的对象const car = { brand: "Toyota", model: "Camry", year: 2023, "is-electric": false, // 属性名包含特殊字符时,需要用引号包裹 start: function () { // 方法 console.log("Engine started!"); },};new Object() 构造函数const person = new Object();person.firstName = "John";person.lastName = "Doe";这种方式不如字面量直观,一般推荐使用字面量语法。
可以通过点记法(dot notation)或方括号记法(bracket notation)来访问对象的属性。
点记法: object.propertyName
console.log(car.brand); // "Toyota"方括号记法: object['propertyName']
方括号记法更灵活,它允许属性名是变量或包含特殊字符。
console.log(car["model"]); // "Camry"console.log(car["is-electric"]); // false
let myKey = "year";console.log(car[myKey]); // 2023, myKey 是一个变量直接对属性赋值即可,如果属性不存在,则会创建该属性。
// 修改属性car.year = 2024;console.log(car.year); // 2024
// 添加新属性car.color = "blue";console.log(car.color); // "blue"使用 delete 操作符可以删除对象的属性。
delete car.color;console.log(car.color); // undefined在对象字面量中,可以使用方括号来包含一个表达式,该表达式的结果将作为属性名。
let prefix = "user_";const user = { [prefix + "id"]: "12345", [prefix + "name"]: "Alice",};
console.log(user.user_id); // '12345'in 操作符: 如果对象的自有属性或原型链上存在该属性,则返回 true。
console.log("brand" in car); // trueconsole.log("toString" in car); // true, 因为 toString 在 Object.prototype 上.hasOwnProperty(): 只检查对象的自有属性(不检查原型链),存在则返回 true。
console.log(car.hasOwnProperty("brand")); // trueconsole.log(car.hasOwnProperty("toString")); // false如果变量名和属性名相同,可以只写一个。
let name = "Bob";let age = 30;
// 原始写法// const person = {// name: name,// age: age// };
// 简写const person = { name, age };console.log(person); // { name: 'Bob', age: 30 }方法是值为函数的属性。
const calculator = { add: function (a, b) { return a + b; }, subtract(a, b) { // ES6 方法简写 return a - b; },};
console.log(calculator.add(5, 3)); // 8console.log(calculator.subtract(5, 3)); // 2this 关键字在对象的方法中,this 关键字指向调用该方法的对象。
const person = { name: "Alice", greet: function () { // 这里的 this 指向 person 对象 console.log("Hello, my name is " + this.name); },};
person.greet(); // 输出: "Hello, my name is Alice"注意: this 的指向是动态的,在箭头函数中表现不同,这部分会在后续深入讲解。
原型是 JavaScript 实现继承的核心机制。理解原型链有助于我们理解对象属性的查找方式和对象之间的关系。
Object, Array,或自定义的 function Person() {}。prototype (原型属性):
prototype 属性。__proto__ (原型链接):
__proto__ 属性。prototype 对象。__proto__ 链接到其原型对象上查找。图示关系:
实例对象.__proto__ === 构造函数.prototype
让我们通过一个例子来理解这个流程。
// 1. 创建一个构造函数function Person(name, age) { this.name = name; this.age = age;}
// 2. 在构造函数的原型对象上添加一个共享方法Person.prototype.sayHello = function () { console.log("Hello, I'm " + this.name);};
// 3. 创建两个 Person 实例const person1 = new Person("Alice", 30);const person2 = new Person("Bob", 25);
// 4. 调用实例自身的属性console.log(person1.name); // "Alice"
// 5. 调用原型上的共享方法person1.sayHello(); // "Hello, I'm Alice"person2.sayHello(); // "Hello, I'm Bob"
// 6. 验证关系console.log(person1.__proto__ === Person.prototype); // trueconsole.log(person2.__proto__ === Person.prototype); // true
// 7. 验证 sayHello 方法的位置console.log(person1.hasOwnProperty("sayHello")); // falseconsole.log(Person.prototype.hasOwnProperty("sayHello")); // true在这个例子中,person1 和 person2 自身都没有 sayHello 方法。当我们调用 person1.sayHello() 时:
person1 对象上查找 sayHello。person1.__proto__ 找到 Person.prototype。Person.prototype 上找到了 sayHello 方法,并执行它。原型对象自身也是一个普通对象,所以它也有自己的 __proto__ 属性,指向它自己的构造函数的原型。这样一层一层链接起来,就形成了一个原型链。
// person1 的原型是 Person.prototypeconsole.log(person1.__proto__); // Person.prototype
// Person.prototype 的原型是 Object.prototypeconsole.log(person1.__proto__.__proto__); // Object.prototype
// Object.prototype 的原型是 null,原型链的终点console.log(person1.__proto__.__proto__.__proto__); // null当你访问 person1.toString() 时:
person1 上查找,没找到。person1.__proto__ 找到 Person.prototype,还是没找到。Person.prototype.__proto__ 找到 Object.prototype,找到了 toString() 方法,执行它。
这就是为什么我们创建的对象,即使什么都不定义,也能调用像 toString(), hasOwnProperty() 这样的方法,因为它们都定义在 Object.prototype 上。
constructor 属性每个原型对象都有一个 constructor 属性,它指回与之关联的构造函数。
console.log(Person.prototype.constructor === Person); // trueconsole.log(person1.constructor === Person); // true// (person1 自身没有 constructor,所以会沿着原型链找到 Person.prototype.constructor)这可以用来检查一个对象的类型或创建新的同类型实例。