new

**new** 运算符允许开发人员创建用户定义的对象类型的实例,或创建具有构造函数的内置对象类型的实例。

试一试

语法

js
new constructor
new constructor()
new constructor(arg1)
new constructor(arg1, arg2)
new constructor(arg1, arg2, /* …, */ argN)

参数

constructor

指定对象实例类型的类或函数。表达式可以是任何具有足够优先级的内容,包括标识符、属性访问器或另一个 new 表达式,但可选链不允许。

arg1arg2、…、argN

constructor 将被调用的值的列表。new Foo 等效于 new Foo(),即如果未指定参数列表,则 Foo 将在没有参数的情况下被调用。

描述

当函数使用 **new** 关键字调用时,该函数将用作构造函数。new 将执行以下操作

  1. 创建一个空白的、普通的 JavaScript 对象。为了方便起见,我们称之为 newInstance
  2. 如果 prototype 是一个Object,则将 newInstance 的 [[Prototype]] 指向构造函数的 prototype 属性。否则,newInstance 将保留为一个普通的对象,其 [[Prototype]] 为 Object.prototype

    注意:添加到构造函数的 prototype 属性的属性/对象因此可供从构造函数创建的所有实例访问。

  3. 使用给定参数执行构造函数,将 newInstance 绑定为this 上下文(即,构造函数中对 this 的所有引用现在都引用 newInstance)。
  4. 如果构造函数返回一个非原始类型,则此返回值将成为整个 new 表达式的结果。否则,如果构造函数没有返回值或返回原始类型,则将返回 newInstance。(通常构造函数不会返回值,但它们可以选择这样做以覆盖正常的对象创建过程。)

只能使用 new 运算符实例化 - 尝试在没有 new 的情况下调用类将抛出 TypeError

使用用户定义的构造函数创建对象需要两个步骤

  1. 通过编写一个指定其名称和属性的函数来定义对象类型。例如,用于创建对象 Foo 的构造函数可能如下所示
    js
    function Foo(bar1, bar2) {
      this.bar1 = bar1;
      this.bar2 = bar2;
    }
    
  2. 使用 new 创建对象的实例。
    js
    const myFoo = new Foo("Bar 1", 2021);
    

注意:对象可以有一个属性,该属性本身是另一个对象。请参阅下面的示例。

您始终可以向先前定义的对象实例添加属性。例如,语句 car1.color = "black"car1 添加了一个名为 color 的属性,并为其分配了值 "black"

但是,这不会影响任何其他对象。要将新属性添加到所有相同类型的对象,您必须将该属性添加到构造函数的 prototype 属性。这定义了一个由使用该函数创建的所有对象共享的属性,而不是仅由对象类型的一个实例共享。以下代码向所有类型为 Car 的对象添加了一个名为 color 的属性,其值为 "original color",然后仅在实例对象 car1 中将该值覆盖为字符串 "black"。有关更多信息,请参阅原型

js
function Car() {}
const car1 = new Car();
const car2 = new Car();

console.log(car1.color); // undefined

Car.prototype.color = "original color";
console.log(car1.color); // 'original color'

car1.color = "black";
console.log(car1.color); // 'black'

console.log(Object.getPrototypeOf(car1).color); // 'original color'
console.log(Object.getPrototypeOf(car2).color); // 'original color'
console.log(car1.color); // 'black'
console.log(car2.color); // 'original color'

注意:虽然构造函数可以像任何普通函数一样被调用(即没有 new 运算符),但在这种情况下,不会创建新对象,this 的值也不同。

函数可以通过检查new.target 来知道它是否是用 new 调用的。new.target 仅在没有 new 调用函数时为 undefined。例如,您可以创建一个函数,当它被调用时与被构造时表现不同

js
function Car(color) {
  if (!new.target) {
    // Called as function.
    return `${color} car`;
  }
  // Called with new.
  this.color = color;
}

const a = Car("red"); // a is "red car"
const b = new Car("red"); // b is `Car { color: "red" }`

在引入的 ES6 之前,大多数 JavaScript 内置函数都是可调用和可构造的,尽管它们中的许多表现出不同的行为。举几个例子

  • Array()Error()Function() 在被调用和被构造时表现相同。
  • Boolean()Number()String() 在被调用时将其参数强制转换为相应的原始类型,在被构造时返回包装对象。
  • Date() 在被调用时返回一个表示当前日期的字符串,等效于 new Date().toString()

在 ES6 之后,语言对哪些是构造函数哪些是函数更加严格。例如

  • Symbol()BigInt() 只能在没有 new 的情况下调用。尝试构造它们将抛出 TypeError
  • ProxyMap 只能使用 new 构造。尝试调用它们将抛出 TypeError

示例

对象类型和对象实例

假设您想为汽车创建一种对象类型。您希望这种类型的对象被称为 Car,并且希望它具有品牌、型号和年份的属性。为此,您将编写以下函数

js
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}

现在,您可以创建名为 myCar 的对象,如下所示

js
const myCar = new Car("Eagle", "Talon TSi", 1993);

此语句创建 myCar 并为其属性分配指定的值。然后,myCar.make 的值为字符串 "Eagle",myCar.year 为整数 1993,等等。

您可以通过调用 new 创建任意数量的 car 对象。例如

js
const kensCar = new Car("Nissan", "300ZX", 1992);

本身是另一个对象的属性

假设您定义了一个名为 Person 的对象,如下所示

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

然后实例化两个新的 Person 对象,如下所示

js
const rand = new Person("Rand McNally", 33, "M");
const ken = new Person("Ken Jones", 39, "M");

然后,您可以重写 Car 的定义以包含一个名为 owner 的属性,该属性接受一个 Person 对象,如下所示

js
function Car(make, model, year, owner) {
  this.make = make;
  this.model = model;
  this.year = year;
  this.owner = owner;
}

要实例化新对象,您将使用以下方法

js
const car1 = new Car("Eagle", "Talon TSi", 1993, rand);
const car2 = new Car("Nissan", "300ZX", 1992, ken);

在创建新对象时,不是传递文字字符串或整数,上面的语句将对象 randken 作为参数传递给所有者。要找出 car2 所有者的姓名,您可以访问以下属性

js
car2.owner.name;

在类中使用 new

js
class Person {
  constructor(name) {
    this.name = name;
  }
  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
}

const p = new Person("Caroline");
p.greet(); // Hello, my name is Caroline

规范

规范
ECMAScript 语言规范
# sec-new-operator

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅