JavaScript 构造函数

摘要: 在本教程中,您将学习关于 JavaScript 构造函数以及如何使用 new 关键字创建对象。

JavaScript 构造函数简介

JavaScript 对象教程 中,您学习了如何使用对象字面量语法创建新对象。

例如,以下代码创建了一个新的 person 对象,它包含两个属性 firstNamelastName

let person = {
    firstName: 'John',
    lastName: 'Doe'
};Code language: JavaScript (javascript)

在实践中,您经常需要创建许多类似于 person 对象的对象。

为此,您可以使用构造函数来定义自定义类型,并使用 new 运算符从该类型创建多个对象。

从技术上讲,构造函数是一个普通的 函数,遵循以下约定

  • 构造函数的名称以大写字母开头,例如 PersonDocument 等。
  • 构造函数只能使用 new 运算符调用。

请注意,ES6 引入了 class 关键字,允许您定义自定义类型。类只是构造函数的语法糖,并提供了一些增强功能。

以下示例定义了一个名为 Person 的构造函数

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}Code language: JavaScript (javascript)

在本例中,Person 与普通函数相同,只是其名称以大写字母 P 开头。

要创建 Person 的新实例,可以使用 new 运算符

let person = new Person('John','Doe');Code language: JavaScript (javascript)

基本上,new 运算符执行以下操作

  • 创建一个新的空对象,并将其分配给 this 变量。
  • 将参数 'John''Doe' 分别分配给对象的 firstNamelastName 属性。
  • 返回 this 值。

它在功能上等效于以下代码

function Person(firstName, lastName) {
    // this = {};

    // add properties to this
    this.firstName = firstName;
    this.lastName = lastName;

    // return this;
}Code language: JavaScript (javascript)

因此,以下语句

let person = new Person('John','Doe');Code language: JavaScript (javascript)

… 返回的结果与以下语句相同

let person = {
    firstName: 'John',
    lastName: 'Doe'
};Code language: JavaScript (javascript)

但是,构造函数 Person 允许您创建多个类似的对象。例如

let person1 = new Person('Jane','Doe')
let person2 = new Person('James','Smith')Code language: JavaScript (javascript)

向 JavaScript 构造函数添加方法

对象可能包含用于操作其数据的方法。要向通过构造函数创建的对象添加方法,可以使用 this 关键字。例如

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;

    this.getFullName = function () {
        return this.firstName + " " + this.lastName;
    };
}Code language: JavaScript (javascript)

现在,您可以创建一个新的 Person 对象并调用 getFullName() 方法

let person = new Person("John", "Doe");
console.log(person.getFullName());Code language: JavaScript (javascript)

输出

John Doe

构造函数的问题在于,当您创建 Person 的多个实例时,this.getFullName() 会在每个实例中重复,这在内存效率方面并不理想。

为了解决这个问题,您可以使用 原型,以便自定义类型的所有实例都可以共享相同的方法。

从构造函数返回

通常,构造函数会隐式返回 this,该值设置为新创建的对象。但如果它包含 return 语句,则存在以下规则

  • 如果使用对象调用 return,则构造函数返回该对象,而不是 this
  • 如果使用对象以外的值调用 return,则会忽略它。

在没有 new 关键字的情况下调用构造函数

从技术上讲,您可以像普通函数一样调用构造函数,而不使用 new 关键字,如下所示

let person = Person('John','Doe');Code language: JavaScript (javascript)

在这种情况下,Person 只是像普通函数一样执行。因此,Person 函数内部的 this 不会绑定到 person 变量,而是绑定到 全局对象

如果您尝试访问 firstNamelastName 属性,则会收到错误

console.log(person.firstName);Code language: CSS (css)

错误

TypeError: Cannot read property 'firstName' of undefinedCode language: JavaScript (javascript)

同样,您也无法访问 getFullName() 方法,因为它绑定到全局对象。

person.getFullName();Code language: CSS (css)

错误

TypeError: Cannot read property 'getFullName' of undefinedCode language: JavaScript (javascript)

为了防止在没有 new 关键字的情况下调用构造函数,ES6 引入了 new.target 属性。

如果使用 new 关键字调用构造函数,则 new.target 返回函数的引用。否则,它返回 undefined

以下代码向 Person 函数添加了一个语句,用于将 new.target 显示到控制台

function Person(firstName, lastName) {
    console.log(new.target);

    this.firstName = firstName;
    this.lastName  = lastName;

    this.getFullName = function () {
        return this.firstName + " " + this.lastName;
    };
}Code language: JavaScript (javascript)

以下代码返回 undefined,因为 Person 构造函数像普通函数一样被调用

let person = Person("John", "Doe");Code language: JavaScript (javascript)

输出

undefinedCode language: JavaScript (javascript)

但是,以下代码返回 Person 函数的引用,因为它使用 new 关键字被调用

let person = new Person("John", "Doe");Code language: JavaScript (javascript)

输出

[Function: Person]Code language: JSON / JSON with Comments (json)

通过使用 new.target,您可以强制构造函数的调用者使用 new 关键字。否则,您可以抛出错误,如下所示

function Person(firstName, lastName) {
    if (!new.target) {
        throw Error("Cannot be called without the new keyword");
    }

    this.firstName = firstName;
    this.lastName = lastName;
}Code language: JavaScript (javascript)

或者,您可以通过在构造函数的调用者没有使用 new 关键字的情况下创建一个新的 Person 对象,使语法更加灵活

function Person(firstName, lastName) {
    if (!new.target) {
        return new Person(firstName, lastName);
    }

    this.firstName = firstName;
    this.lastName = lastName;
}

let person = Person("John", "Doe");

console.log(person.firstName);Code language: JavaScript (javascript)

这种模式经常在 JavaScript 库和框架中使用,以使语法更加灵活。

总结

  • JavaScript 构造函数是一个普通的函数,用于创建多个类似的对象。
本教程是否有帮助?