Bootstrap

面试题:对象创建的方式有哪些?

在 JavaScript 中,对象可以通过多种方式创建。每种方法都有其特点和适用场景。

在这里插入图片描述

以下是几种常见的对象创建方式:

1. 对象字面量(Object Literal)

这是最简单和直接的方式,通过大括号 {} 来定义一个对象,并且可以立即为对象添加属性和方法。

const person = {
  name: 'Alice',
  age: 30,
  sayHello: function() {
    console.log('Hello, my name is ' + this.name);
  }
};

person.sayHello(); // 输出: Hello, my name is Alice

优点:简洁、易读,适合创建简单的对象。

2. 构造函数(Constructor Function)

通过定义一个构造函数,并使用 new 关键字来创建对象实例。构造函数可以用来初始化对象的属性和方法。

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.sayHello = function() {
    console.log('Hello, my name is ' + this.name);
  };
}

const alice = new Person('Alice', 30);
alice.sayHello(); // 输出: Hello, my name is Alice

优点:可以创建多个具有相同结构的对象实例,适合需要创建多个相似对象的场景。

3. 原型式对象创建(Prototype-based Object Creation)

利用 Object.create() 方法,基于现有的对象创建新对象。新对象的原型将指向传入的对象。

const personPrototype = {
  sayHello: function() {
    console.log('Hello, my name is ' + this.name);
  }
};

const alice = Object.create(personPrototype);
alice.name = 'Alice';
alice.age = 30;

alice.sayHello(); // 输出: Hello, my name is Alice

优点:可以显式地控制对象的原型链,适合需要共享方法或属性的场景。

4. 类(Class)

ES6 引入了 class 语法糖,使得对象的创建更加面向对象化。实际上,class 仍然是基于原型的,但它提供了一种更接近传统面向对象语言的语法。

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

  sayHello() {
    console.log('Hello, my name is ' + this.name);
  }
}

const alice = new Person('Alice', 30);
alice.sayHello(); // 输出: Hello, my name is Alice

优点:语法简洁,易于理解和使用,特别适合面向对象编程。

5. 工厂函数(Factory Function)

工厂函数是一种设计模式,它通过一个函数返回一个新的对象。工厂函数可以根据不同的条件创建不同类型的对象。

function createPerson(name, age) {
  return {
    name: name,
    age: age,
    sayHello: function() {
      console.log('Hello, my name is ' + this.name);
    }
  };
}

const alice = createPerson('Alice', 30);
alice.sayHello(); // 输出: Hello, my name is Alice

优点:可以封装对象的创建逻辑,适合需要根据不同条件创建不同对象的场景。

6. 构造函数与原型结合

为了提高性能和避免重复创建方法,可以将方法定义在构造函数的原型上,而不是每个实例中。

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.sayHello = function() {
  console.log('Hello, my name is ' + this.name);
};

const alice = new Person('Alice', 30);
alice.sayHello(); // 输出: Hello, my name is Alice

优点:方法只会在原型上创建一次,所有实例共享这些方法,节省内存。

7. 动态属性创建

可以在对象创建后动态地添加或修改属性。

const person = {};
person.name = 'Alice';
person.age = 30;

person.sayHello = function() {
  console.log('Hello, my name is ' + this.name);
};

person.sayHello(); // 输出: Hello, my name is Alice

优点:灵活性高,适合需要在运行时动态添加或修改属性的场景。

8. 使用 Object.assign() 创建对象

Object.assign() 可以用于将一个或多个源对象的可枚举属性复制到目标对象,并返回目标对象。

const defaults = {
  name: 'Unknown',
  age: 0
};

const alice = Object.assign({}, defaults, { name: 'Alice', age: 30 });

console.log(alice); // 输出: { name: 'Alice', age: 30 }

优点:方便合并多个对象的属性,适合需要组合多个对象的场景。

9. 使用 Object.defineProperty()Object.defineProperties()

这些方法允许你更细粒度地控制对象属性的行为,例如设置属性的可枚举性、可配置性和可写性。

const person = {};

Object.defineProperty(person, 'name', {
  value: 'Alice',
  writable: true,
  enumerable: true,
  configurable: true
});

Object.defineProperty(person, 'age', {
  value: 30,
  writable: false, // 不能修改
  enumerable: true,
  configurable: true
});

console.log(person.name); // 输出: Alice
person.age = 40; // 不会生效
console.log(person.age);  // 输出: 30

优点:提供了对属性行为的精细控制,适合需要严格管理对象属性的场景。

10. 使用 Proxy 对象

Proxy 允许你创建一个代理对象,拦截并自定义基本操作(如属性访问、赋值等)。这可以用于实现复杂的对象行为。

const handler = {
  get(target, prop, receiver) {
    console.log(`Getting ${prop}`);
    return Reflect.get(target, prop, receiver);
  },
  set(target, prop, value, receiver) {
    console.log(`Setting ${prop} to ${value}`);
    return Reflect.set(target, prop, value, receiver);
  }
};

const person = new Proxy({}, handler);

person.name = 'Alice'; // 输出: Setting name to Alice
console.log(person.name); // 输出: Getting name
                          //        Alice

优点:提供了强大的元编程能力,适合需要拦截和自定义对象行为的高级场景。

总结

  • 对象字面量 是最简单的方式,适合创建简单的对象。
  • 构造函数 提供了面向对象的编程方式,适合创建多个相似的对象实例。
  • Object.create()原型式对象创建 适合需要显式控制原型链的场景。
  • 工厂函数 适合需要根据不同条件创建不同对象的场景。
  • 构造函数与原型结合 可以提高性能,避免重复创建方法。
  • 动态属性创建 提供了高度的灵活性。
  • Object.assign() 适合合并多个对象的属性。
  • Object.defineProperty()Object.defineProperties() 提供了对属性行为的精细控制。
  • Proxy 提供了强大的元编程能力,适合需要拦截和自定义对象行为的场景。

选择哪种方式取决于具体的项目需求和代码风格。对于现代 JavaScript 开发,推荐使用 class 语法糖和 Object.create(),因为它们既简洁又功能强大。

;