JavaScript 对象深入解析(面试题版)

134 阅读3分钟

JavaScript 对象是 JavaScript 中最基础、最强大的数据结构之一。它是一个无序的键值对集合,其中键(属性名)是字符串(或者是可以转为字符串的值),而值(属性值)可以是任何 JavaScript 数据类型。

1. 什么是 JavaScript 对象?

简单来说,对象是**键值对(key-value pairs)**的集合。其中,键(key)是字符串(或 Symbol),值(value)可以是任何 JavaScript 数据类型,包括:

基本数据类型:字符串、数字、布尔值、null、undefined、Symbol、BigInt 其他对象:包括数组、函数、以及其他自定义对象

1.1 对象的定义

在 JavaScript 中,定义一个对象通常有以下两种方式:

  • 字面量方式:

    const person = {
      name: 'John',
      age: 30,
      greet: function() {
        console.log(`Hello, my name is ${this.name}`);
      }
    };
    
  • 构造函数方式:

    const person = new Object();
    person.name = 'John';
    person.age = 30;
    person.greet = function() {
      console.log(`Hello, my name is ${this.name}`);
    };
    
  • **Object.create() **方法: 创建一个新对象,使用现有对象作为新对象的原型。

    const proto = {
      greet: function() {
        console.log("Hello!");
      }
    };

    const obj = Object.create(proto);
    obj.name = "王五";
  • ES6 的类(Class): 虽然本质上仍然是基于原型继承,但提供了更接近传统面向对象语言的语法
class Person {
  constructor(name, age, city) {
    this.name = name;
    this.age = age;
    this.city = city;
  }

  greet() {
    console.log(`你好,我是 ${this.name}`);
  }
}

const person2 = new Person("赵六", 40, "广州");

1.2 对象的基本特性

  • 无序性:对象的属性在内部是无序的,尽管在某些 JavaScript 引擎中(如 Chrome 的 V8 引擎)会以插入顺序来显示属性,但我们不能依赖这个行为,特别是在设计跨浏览器的应用时。
  • 键是字符串类型:即使你给对象的属性使用其他数据类型(如数字或符号),JavaScript 会将它们自动转换为字符串。
  • 值可以是任意数据类型:对象的值可以是任意类型,包括其他对象、数组、函数等。

2. 对象的常见操作

2.1 访问对象属性

有两种方式可以访问对象的属性:

  • 点语法

    console.log(person.name); // 'John'
    
  • 方括号语法(可以动态指定属性名,适用于属性名是动态的情况):

    console.log(person['age']); // 30
    

2.2 添加、修改和删除属性

  • 添加属性

    person.job = 'Developer';
    
  • 修改属性

    person.age = 31;
    
  • 删除属性

    delete person.job;
    
  • Object.keys() 方法: 返回一个包含对象所有可枚举属性名的数组。

    const keys = Object.keys(person);
    keys.forEach(key => console.log(key + ":" + person[key]));
    
  • Object.values() 方法: 返回一个包含对象所有可枚举属性值的数组。

  • Object.entries() 方法: 返回一个包含对象所有可枚举属性的 [key, value] 键值对数组。

2.3 检查属性是否存在

  • in 运算符:检查对象是否包含某个属性。

    console.log('name' in person); // true
    console.log('job' in person);  // false
    
  • hasOwnProperty 方法:检查对象本身是否拥有某个属性,而不是继承自原型链。

    console.log(person.hasOwnProperty('name')); // true
    

3. 原型链与继承

JavaScript 中的对象是通过原型链继承来共享行为和属性的。每个对象都有一个 __proto__ 属性,指向它的原型对象。

3.1 原型链

  • 每个对象都有一个与之关联的原型对象(Prototype),当你访问一个对象的属性时,JavaScript 引擎会首先查找对象本身是否有该属性。如果没有,它会查找对象的原型,再找原型的原型,直到达到 null(即原型链的顶端)。

  • Object.prototype 是所有对象的原型链的顶端。

3.2 继承和原型

  • 当你通过构造函数创建对象时,新的对象会继承构造函数的原型。

    function Person(name, age) {
      this.name = name;
      this.age = age;
    }
    
    Person.prototype.greet = function() {
      console.log(`Hello, my name is ${this.name}`);
    };
    
    const john = new Person('John', 30);
    john.greet(); // 'Hello, my name is John'
    

3.3 Object.create 创建对象

Object.create(proto) 方法创建一个新对象,并将指定的对象作为新对象的原型。

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

const john = Object.create(personProto);
john.name = 'John';
john.greet(); // 'Hello, my name is John'

4. JavaScript 对象的特殊类型

4.1 Object 构造函数

Object 是一个内置构造函数,它可以用来创建空对象,或者将其他数据类型转换为对象。

  • 创建空对象

    const obj = new Object();
    
  • 转换为对象

    const str = 'hello';
    const obj = new Object(str);
    console.log(obj); // String { 'hello' }
    

4.2 Symbol 类型的属性

Symbol 是 ES6 引入的一种基本数据类型,通常用于给对象添加唯一的属性键,以避免命名冲突。每个 Symbol 值都是唯一的。

const sym = Symbol('id');
const obj = {
  [sym]: 123
};
console.log(obj[sym]); // 123

4.3 gettersetter

JavaScript 允许通过 getset 方法在对象上定义计算属性,这些属性不需要明确地存储数据,而是根据其他属性来计算。

const person = {
  firstName: 'John',
  lastName: 'Doe',
  get fullName() {
    return this.firstName + ' ' + this.lastName;
  },
  set fullName(name) {
    const [firstName, lastName] = name.split(' ');
    this.firstName = firstName;
    this.lastName = lastName;
  }
};

console.log(person.fullName); // 'John Doe'
person.fullName = 'Jane Smith';
console.log(person.firstName); // 'Jane'
console.log(person.lastName); // 'Smith'

5. 对象与 JSON

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它基于 JavaScript 对象字面量的语法。

  • JSON.stringify():将 JavaScript 对象转换为 JSON 字符串。

  • JSON.parse():将 JSON 字符串解析为 JavaScript 对象。

6. 常见面试题

5.1 面试题:什么是 JavaScript 对象,如何创建一个对象?

  • 回答:JavaScript 对象是由一组键值对组成的数据结构。你可以使用对象字面量或者 new Object() 来创建一个对象。

    const obj = { key: 'value' };
    // 或者
    const obj = new Object();
    obj.key = 'value';
    

5.2 面试题:如何判断一个对象是否具有某个属性?

  • 回答
    • 使用 in 运算符:

      'property' in obj;
      
    • 使用 hasOwnProperty() 方法判断是否为对象本身的属性:

      obj.hasOwnProperty('property');
      

5.3 面试题:什么是原型链?如何访问一个对象的原型?

  • 回答:原型链是 JavaScript 中对象之间继承的机制。当你访问对象的属性时,如果对象本身没有该属性,JavaScript 引擎会查找对象的原型,再查找原型的原型,直到找到属性或到达 null。可以通过 __proto__ 来访问一个对象的原型。

    const person = { name: 'John' };
    console.log(person.__proto__); // Object.prototype
    

5.4 面试题:如何用 Object.create() 创建一个新对象?

  • 回答Object.create(proto) 用来创建一个新对象,并将指定的对象作为新对象的原型。

    const animal = {
      eat() {
        console.log('Eating');
      }
    };
    
    const dog = Object.create(animal);
    dog.bark = function() {
      console.log('Barking');
    };
    
    dog.eat(); // Eating
    dog.bark(); // Barking
    

5.5 面试题:JavaScript 对象的属性存储在哪里,如何优化性能?

  • 回答:JavaScript 对象的属性通常存储在哈希表中。为了优化性能,可以通过避免频繁地向对象添加和删除属性,避免对象的频繁变化,或者使用原型链来复用属性和方法。

5.6 面试题:gettersetter 是什么?如何使用它们?

  • 回答gettersetter 是用于定义对象的属性访问器的特殊方法。getter 用于访问属性值,setter 用于修改属性值。

    const person = {
      firstName: 'John',
      lastName: 'Doe',
      get fullName() {
        return this.firstName + ' ' + this.lastName;
      },
      set full