为什么我们现在应该停止使用的JavaScript构造器

JavaScript通过保留旧结构来保持与旧应用程序的向后兼容性。

在大多数情况下,它们都是过时的,并被更简洁、更有表现力的新代码结构所取代。

在本文中,我们将介绍应该立即停止使用或尽量减少使用的旧结构。

为什么我们现在应该停止使用的JavaScript构造器

1.传统函数

传统函数是以关键字 function 开头的函数。

我们不应该再频繁地使用它们,因为我们已经将类语法作为语法糖,用于构造函数和不关心 this 的箭头函数。

提升

传统函数还带有2个不同的变体——函数声明和函数表达式。

函数声明是这样写的函数:

function foo() {
console.log('foo');
}

它们被提升,这意味着它们被提升到代码的顶部,或者被JavaScript解释器自动提升。

因此,我们可以在定义之前调用它们:

foo();
function foo() {
console.log('foo');
}

函数表达式是分配给变量的传统函数:

const foo = function() {
console.log('foo');
}

如果在定义函数表达式之前调用它们,则会出现错误。

这令人困惑且难以记住。因此,在编写传统函数时容易出错。

为什么我们现在应该停止使用的JavaScript构造器

继承

构造函数的功能也很混乱,因为需要操纵原型。

例如,我们必须编写:

function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.fullName = function() {
return `${this.firstName} ${this.lastName}`;
}

创建一个构造函数,然后向其添加一个实例方法。

Person.prototype.fullNamePerson 的实例方法,而 Person 是构造函数。

同样,可以像其他面向对象的语言一样,使用 new 关键字实例化构造函数,但是我们使用函数而不是类来实例化它们,这更加令人困惑。

如果我们必须进行继承,那么事情会变得更加混乱和容易出错。

例如,如果我们想创建一个 Employee 构造函数来扩展上面的 Person 构造函数,我们必须编写:

function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.fullName = function() {
return `${this.firstName} ${this.lastName}`;
}
function Employee(firstName, lastName, employeeCode) {
Person.call(this, firstName, lastName)
this.employeeCode = employeeCode;
}
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.getEmployeeCode = function() {
return this.employeeCode;
}

它有很多部分,但是我们必须要写这一句:

Person.call(this, firstName, lastName)

Employee 构造函数中使用 call 方法调用 Person 构造函数。

然后,我们必须使用 Object.create 来设置 Employee 的原型,以扩展 Person 的原型。

要添加 Employee 专有的实例,我们必须像使用 getEmployeeCode 一样将方法添加到 Employee 的原型中。

如果缺少这些步骤中的任何一个,我们将不会收到警告或错误。因此,很容易犯错误。

为什么我们现在应该停止使用的JavaScript构造器

类语法

类语法解决了以上所有问题。无法提升它们,方法保留在类中,并且我们具有 extends 关键字可以从现有类继承,而 super 可以调用超类的构造函数。

如果我们的类扩展了另一个类,而忘记了调用 super 则会出错。

例如,我们可以使用类语法重写 Person 构造函数,如下所示:

class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
fullName() {
return `${this.firstName} ${this.lastName}`;
}
}

如果在定义之前引用它,我们会得到一个错误,另外 fullName 方法位于类内部,而不是 Person 原型的属性。

下面是一样的,但是要干净得多。

为了创建扩展 Person 类的 Employee 类,我们编写:

class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
class Employee extends Person {
constructor(firstName, lastName) {
super(firstName, lastName)
}
getEmployeeCode() {
return this.employeeCode;
}
}

正如我们所看到的,我们只是添加了 extends 关键字,即调用父类的构造函数的 super 调用,然后将我们自己的getEmployeeCode 方法添加到 Employee 类。

如果我们在类声明中有 extends,那么如果我们错过了 super,就会得到错误。

为什么我们现在应该停止使用的JavaScript构造器

2.参数对象

在拥有 rest 运算符之前,我们必须使用 arguments 对象将所有参数传递到函数中。

arguments 对象是一个类似数组的对象,这意味着它有索引和 length 属性,但它没有任何数组方法。

同样,这是非常具有欺骗性的。

使用rest运算符,我们得到一个返回的数组,因此我们可以使用数组方法。

例如,我们可以这样写:

const foo = (...args) => console.log(args.join(','))
foo(1, 2, 3, 4, 5);

... args 是其余操作,3个点是rest运算符,args传入了参数数组。

我们可以在 args 上调用 join 方法,因此我们知道它是一个数组。

它也适用于传统函数和箭头函数,而 arguments 不适用于箭头函数。

总结

旧的结构被保存在JavaScript中,这样旧的应用程序就不会崩溃。然而,这并不意味着我们应该使用它们。

我们应该开始使用新的结构,比如类语法和rest操作符,因为它们更简洁、更少欺骗和混淆。

您可能还会对下面的文章感兴趣: