返回

不使用 `new`,创建对象实例的艺术

javascript

无需 new ,创建对象实例的艺术

导言

在 JavaScript 中,构造函数通常使用 new 关键字调用,这会创建一个新对象并设置 this 上下文。然而,有时,你可能需要在不使用 new 关键字的情况下创建对象实例,同时传递任意数量的参数给构造函数。本文将深入探讨是否可行,以及如何实现。

问题陈述

假设我们有一个构造函数 Something,它需要初始化一些属性。此外,我们希望创建一个函数 createSomething,它可以接收任意数量的参数并将其传递给 Something 构造函数,而无需使用 new 关键字。理想情况下,代码应该如下所示:

function Something() {
  // 初始化属性
}

function createSomething() {
  return new Something.apply(null, arguments);
}

const s = createSomething(a, b, c); // 's' 是 Something 的实例

但这段代码实际上无法工作,因为 JavaScript 中没有内置方法可以将 .apply()new 运算符一起调用。

变通方案

1. 工厂函数

我们可以创建一个工厂函数 createSomething,它返回一个新对象的实例。该函数接收构造函数和参数数组,并使用 apply() 将参数传递给构造函数。

const createSomething = (Ctor, ...args) => {
  return new Ctor.apply(null, args);
};

2. bind()apply()

我们可以使用 bind() 方法将构造函数绑定到一个特定的参数,然后使用 apply() 传递其余参数。

const createSomething = (Ctor) => {
  return function(...args) {
    return new Ctor.bind(null).apply(null, args);
  };
};

3. ES6 类

ES6 引入了类,它为创建对象实例提供了另一种方式。我们可以创建一个类 Something,它具有一个静态 create 方法,该方法返回一个新对象的实例。

class Something {
  static create(...args) {
    return new this(...args);
  }
}

4. 自定义函数

我们可以创建一个自定义函数 F,它将构造函数和参数数组作为输入,并返回一个新对象的实例。

const createSomething = (Ctor, ...args) => {
  function F() {
    return Ctor.apply(this, args);
  }

  F.prototype = Ctor.prototype;

  return new F();
};

选择最佳方法

不同的变通方案适用于不同的情况。

  • 工厂函数是最简单的方法,但它不能与子类一起使用。
  • bind()apply() 比较灵活,但它需要一些额外的代码。
  • ES6 类提供了最干净的语法,但它需要兼容较旧的 JavaScript 环境。
  • 自定义函数是最通用的方法,但它也最复杂。

结论

虽然 JavaScript 中没有内置方法可以直接使用 .apply()new 运算符一起调用,但通过使用工厂函数、bind()apply()、ES6 类或自定义函数,我们可以实现同样的效果,在不使用 new 关键字的情况下创建对象实例并传递任意数量的参数给构造函数。

常见问题解答

  1. 为什么要在不使用 new 关键字的情况下创建对象实例?
    • 有时,第三方库或代码段可能需要你以这种方式创建对象。
  2. 工厂函数、bind()apply()、ES6 类和自定义函数有什么区别?
    • 不同的方法提供了不同的权衡,包括简单性、灵活性、兼容性和通用性。
  3. 哪种方法最好?
    • 最佳方法取决于具体情况和优先级。
  4. 除了提到的方法之外,还有其他方法吗?
    • 有些人还使用了其他技术,例如代理和元编程,但它们更高级,不太常用。
  5. 在使用这些方法时需要注意哪些注意事项?
    • 确保构造函数的 prototype 属性正确设置,尤其是在使用自定义函数时。