Skip to content

JavaScript 对象

对象的概念

对象是 JavaScript 中的核心概念之一,它是一种复合数据类型,用于存储键值对。对象可以包含属性(数据)和方法(函数),是 JavaScript 中组织和管理数据的重要方式。

对象的创建

1. 对象字面量

对象字面量是创建对象最常用的方式,使用花括号 {}

javascript
const person = {
  name: "John",
  age: 30,
  greet: function () {
    return `Hello, my name is ${this.name}!`;
  },
  // 简写方法
  sayHello() {
    return `Hello, I'm ${this.name}!`;
  },
};

console.log(person.name); // 输出: John
console.log(person.age); // 输出: 30
console.log(person.greet()); // 输出: Hello, my name is John!
console.log(person.sayHello()); // 输出: Hello, I'm John!

2. 构造函数

使用构造函数创建对象:

javascript
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.greet = function () {
    return `Hello, my name is ${this.name}!`;
  };
}

const person = new Person("John", 30);
console.log(person.name); // 输出: John
console.log(person.age); // 输出: 30
console.log(person.greet()); // 输出: Hello, my name is John!

3. Object 构造函数

使用 Object 构造函数创建对象:

javascript
const person = new Object();
person.name = "John";
person.age = 30;
person.greet = function () {
  return `Hello, my name is ${this.name}!`;
};

console.log(person.name); // 输出: John
console.log(person.age); // 输出: 30
console.log(person.greet()); // 输出: Hello, my name is John!

4. Object.create()

使用 Object.create() 方法创建对象,指定原型:

javascript
const personProto = {
  greet: function () {
    return `Hello, my name is ${this.name}!`;
  },
};

const person = Object.create(personProto);
person.name = "John";
person.age = 30;

console.log(person.name); // 输出: John
console.log(person.age); // 输出: 30
console.log(person.greet()); // 输出: Hello, my name is John!

5. 类

使用 ES6 引入的 class 关键字创建对象:

javascript
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    return `Hello, my name is ${this.name}!`;
  }
}

const person = new Person("John", 30);
console.log(person.name); // 输出: John
console.log(person.age); // 输出: 30
console.log(person.greet()); // 输出: Hello, my name is John!

对象的属性

1. 访问属性

使用点号 . 或方括号 [] 访问对象的属性:

javascript
const person = {
  name: "John",
  age: 30,
};

// 使用点号访问
console.log(person.name); // 输出: John

// 使用方括号访问
console.log(person["age"]); // 输出: 30

// 方括号内可以是变量
const propName = "name";
console.log(person[propName]); // 输出: John

// 方括号内可以是表达式
const prefix = "na";
const suffix = "me";
console.log(person[prefix + suffix]); // 输出: John

2. 添加属性

使用点号 . 或方括号 [] 添加新属性:

javascript
const person = {
  name: "John",
};

// 使用点号添加
person.age = 30;

// 使用方括号添加
person["city"] = "New York";

console.log(person); // 输出: { name: 'John', age: 30, city: 'New York' }

3. 修改属性

使用点号 . 或方括号 [] 修改现有属性:

javascript
const person = {
  name: "John",
  age: 30,
};

// 使用点号修改
person.age = 31;

// 使用方括号修改
person["name"] = "Jane";

console.log(person); // 输出: { name: 'Jane', age: 31 }

4. 删除属性

使用 delete 操作符删除属性:

javascript
const person = {
  name: "John",
  age: 30,
  city: "New York",
};

// 删除属性
delete person.city;

console.log(person); // 输出: { name: 'John', age: 30 }
console.log("city" in person); // 输出: false

5. 检查属性

使用 in 操作符检查对象是否包含某个属性:

javascript
const person = {
  name: "John",
  age: 30,
};

console.log("name" in person); // 输出: true
console.log("age" in person); // 输出: true
console.log("city" in person); // 输出: false

使用 hasOwnProperty() 方法检查对象是否有自己的属性(不包括继承的属性):

javascript
const person = {
  name: "John",
  age: 30,
};

console.log(person.hasOwnProperty("name")); // 输出: true
console.log(person.hasOwnProperty("age")); // 输出: true
console.log(person.hasOwnProperty("toString")); // 输出: false(继承的方法)

使用 Object.hasOwn() 方法(ES2022+)检查对象是否有自己的属性:

javascript
const person = {
  name: "John",
  age: 30,
};

console.log(Object.hasOwn(person, "name")); // 输出: true
console.log(Object.hasOwn(person, "age")); // 输出: true
console.log(Object.hasOwn(person, "toString")); // 输出: false(继承的方法)

对象的方法

1. 定义方法

在对象中定义方法:

javascript
const person = {
  name: "John",
  // 传统方法
  greet: function () {
    return `Hello, my name is ${this.name}!`;
  },
  // 简写方法
  sayHello() {
    return `Hello, I'm ${this.name}!`;
  },
  // 箭头函数方法(注意:箭头函数没有自己的 this)
  introduce: () => {
    // 这里的 this 指向外部作用域
    return `Hello, my name is ${this.name}!`;
  },
};

console.log(person.greet()); // 输出: Hello, my name is ${this.name}!
console.log(person.sayHello()); // 输出: Hello, I'm ${this.name}!
console.log(person.introduce()); // 输出: Hello, my name is undefined!(箭头函数中的 this 指向全局对象)

2. this 关键字

在方法中,this 关键字指向调用该方法的对象:

javascript
const person = {
  name: "John",
  greet: function () {
    return `Hello, my name is ${this.name}!`;
  },
};

console.log(person.greet()); // 输出: Hello, my name is John!(this 指向 person)

// 将方法赋值给变量
const greet = person.greet;
console.log(greet()); // 输出: Hello, my name is undefined!(this 指向全局对象)

// 使用 bind 绑定 this
const boundGreet = person.greet.bind(person);
console.log(boundGreet()); // 输出: Hello, my name is John!(this 绑定到 person)

// 使用 call 调用
console.log(person.greet.call({ name: "Jane" })); // 输出: Hello, my name is Jane!(this 指向传入的对象)

// 使用 apply 调用
console.log(person.greet.apply({ name: "Bob" })); // 输出: Hello, my name is Bob!(this 指向传入的对象)

对象的遍历

1. for...in 循环

使用 for...in 循环遍历对象的可枚举属性:

javascript
const person = {
  name: "John",
  age: 30,
  city: "New York",
};

for (const key in person) {
  console.log(`${key}: ${person[key]}`);
}
// 输出:
// name: John
// age: 30
// city: New York

// 遍历包括继承的属性
const person2 = Object.create(person);
person2.job = "Engineer";

for (const key in person2) {
  console.log(`${key}: ${person2[key]}`);
}
// 输出:
// job: Engineer
// name: John
// age: 30
// city: New York

// 使用 hasOwnProperty 过滤继承的属性
for (const key in person2) {
  if (person2.hasOwnProperty(key)) {
    console.log(`${key}: ${person2[key]}`);
  }
}
// 输出:
// job: Engineer

2. Object.keys()

Object.keys() 方法返回对象自身的可枚举属性名数组:

javascript
const person = {
  name: "John",
  age: 30,
  city: "New York",
};

const keys = Object.keys(person);
console.log(keys); // 输出: ["name", "age", "city"]

// 遍历属性
keys.forEach((key) => {
  console.log(`${key}: ${person[key]}`);
});
// 输出:
// name: John
// age: 30
// city: New York

3. Object.values()

Object.values() 方法返回对象自身的可枚举属性值数组:

javascript
const person = {
  name: "John",
  age: 30,
  city: "New York",
};

const values = Object.values(person);
console.log(values); // 输出: ["John", 30, "New York"]

4. Object.entries()

Object.entries() 方法返回对象自身的可枚举属性的键值对数组:

javascript
const person = {
  name: "John",
  age: 30,
  city: "New York",
};

const entries = Object.entries(person);
console.log(entries); // 输出: [["name", "John"], ["age", 30], ["city", "New York"]]

// 遍历键值对
entries.forEach(([key, value]) => {
  console.log(`${key}: ${value}`);
});
// 输出:
// name: John
// age: 30
// city: New York

对象的操作方法

1. Object.assign()

Object.assign() 方法用于将所有可枚举的自有属性从一个或多个源对象复制到目标对象:

javascript
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3, a: 4 };

const result = Object.assign(target, source1, source2);
console.log(result); // 输出: { a: 4, b: 2, c: 3 }
console.log(target); // 输出: { a: 4, b: 2, c: 3 }(目标对象被修改)

// 浅拷贝对象
const person = { name: "John", age: 30 };
const copy = Object.assign({}, person);
console.log(copy); // 输出: { name: 'John', age: 30 }
console.log(copy === person); // 输出: false(不同的对象)

2. Object.create()

Object.create() 方法创建一个新对象,使用现有的对象作为新对象的原型:

javascript
const person = {
  name: "John",
  age: 30,
  greet: function () {
    return `Hello, my name is ${this.name}!`;
  },
};

// 创建以 person 为原型的新对象
const employee = Object.create(person);
employee.job = "Engineer";
employee.salary = 50000;

console.log(employee.name); // 输出: John(继承自原型)
console.log(employee.job); // 输出: Engineer(自身属性)
console.log(employee.greet()); // 输出: Hello, my name is John!(继承自原型)

3. Object.freeze()

Object.freeze() 方法冻结对象,防止添加、删除或修改属性:

javascript
const person = {
  name: "John",
  age: 30,
};

Object.freeze(person);

// 尝试修改属性(无效果)
person.age = 31;
console.log(person.age); // 输出: 30

// 尝试添加属性(无效果)
person.city = "New York";
console.log(person.city); // 输出: undefined

// 尝试删除属性(无效果)
delete person.name;
console.log(person.name); // 输出: John

// 检查对象是否被冻结
console.log(Object.isFrozen(person)); // 输出: true

4. Object.seal()

Object.seal() 方法密封对象,防止添加或删除属性,但可以修改现有属性:

javascript
const person = {
  name: "John",
  age: 30,
};

Object.seal(person);

// 尝试修改属性(有效)
person.age = 31;
console.log(person.age); // 输出: 31

// 尝试添加属性(无效果)
person.city = "New York";
console.log(person.city); // 输出: undefined

// 尝试删除属性(无效果)
delete person.name;
console.log(person.name); // 输出: John

// 检查对象是否被密封
console.log(Object.isSealed(person)); // 输出: true

5. Object.defineProperty()

Object.defineProperty() 方法定义对象的属性:

javascript
const person = {
  name: "John",
};

// 定义新属性
Object.defineProperty(person, "age", {
  value: 30,
  writable: true, // 是否可写
  enumerable: true, // 是否可枚举
  configurable: true, // 是否可配置
});

console.log(person.age); // 输出: 30

// 修改属性
Object.defineProperty(person, "age", {
  writable: false, // 设置为不可写
});

// 尝试修改属性(无效果)
person.age = 31;
console.log(person.age); // 输出: 30

// 定义 getter 和 setter
Object.defineProperty(person, "fullName", {
  get: function () {
    return this.name;
  },
  set: function (value) {
    this.name = value;
  },
  enumerable: true,
  configurable: true,
});

console.log(person.fullName); // 输出: John
person.fullName = "Jane";
console.log(person.name); // 输出: Jane
console.log(person.fullName); // 输出: Jane

对象的最佳实践

1. 使用对象字面量

优先使用对象字面量创建对象:

javascript
// 好的做法
const person = {
  name: "John",
  age: 30,
};

// 不好的做法
const person = new Object();
person.name = "John";
person.age = 30;

2. 使用简写方法

使用 ES6 引入的简写方法语法:

javascript
// 好的做法
const person = {
  name: "John",
  greet() {
    return `Hello, my name is ${this.name}!`;
  },
};

// 不好的做法
const person = {
  name: "John",
  greet: function () {
    return `Hello, my name is ${this.name}!`;
  },
};

3. 使用计算属性名

使用 ES6 引入的计算属性名:

javascript
const propName = "name";
const person = {
  [propName]: "John",
  [`${propName}Length`]: 4,
};

console.log(person); // 输出: { name: 'John', nameLength: 4 }

4. 避免使用 this 在箭头函数中

避免在对象方法中使用箭头函数,因为箭头函数没有自己的 this

javascript
// 不好的做法
const person = {
  name: "John",
  greet: () => {
    return `Hello, my name is ${this.name}!`; // this 指向外部作用域
  },
};

// 好的做法
const person = {
  name: "John",
  greet() {
    return `Hello, my name is ${this.name}!`; // this 指向 person
  },
};

5. 使用 Object.assign() 或扩展运算符复制对象

使用 Object.assign() 或扩展运算符 {...} 复制对象:

javascript
const person = {
  name: "John",
  age: 30,
};

// 使用 Object.assign()
const copy1 = Object.assign({}, person);

// 使用扩展运算符
const copy2 = { ...person };

console.log(copy1); // 输出: { name: 'John', age: 30 }
console.log(copy2); // 输出: { name: 'John', age: 30 }

6. 合理使用 getter 和 setter

使用 getter 和 setter 控制属性的访问和修改:

javascript
const person = {
  _name: "John",
  get name() {
    return this._name;
  },
  set name(value) {
    if (typeof value === "string" && value.length > 0) {
      this._name = value;
    } else {
      console.error("Name must be a non-empty string");
    }
  },
};

console.log(person.name); // 输出: John
person.name = "Jane";
console.log(person.name); // 输出: Jane
person.name = ""; // 输出: Name must be a non-empty string

小结

对象是 JavaScript 中的核心概念,它是一种复合数据类型,用于存储键值对。JavaScript 提供了多种创建对象的方式,包括对象字面量、构造函数、Object 构造函数、Object.create() 和类。对象具有属性和方法,可以通过点号或方括号访问、添加、修改和删除属性。对象的遍历可以使用 for...in 循环、Object.keys()、Object.values() 和 Object.entries() 等方法。此外,JavaScript 还提供了多种对象操作方法,如 Object.assign()、Object.create()、Object.freeze()、Object.seal() 和 Object.defineProperty() 等。在实际开发中,应该遵循最佳实践,使用合适的方式创建和操作对象,提高代码的可读性和可维护性。