ES2015(ES6)`class`语法有什么好处?


87

我对ES6类有很多疑问。

使用class语法有什么好处?我读到public / private / static将成为ES7的一部分,这是原因吗?

而且,是class一种不同的OOP还是JavaScript的原型继承?我可以使用修改它.prototype吗?还是只是同一对象,而是两种不同的声明方法。

有速度好处吗?如果您拥有大型应用程序(例如大型应用程序),可能更容易维护/理解?


只是我个人的看法:较新的语法更容易理解,不需要太多的经验(特别是因为大多数人都来自OOP语言)。但是原型对象模型是值得了解的,并且您将永远不会学习使用新语法,除非您已经知道,否则处理原型代码将更具挑战性。所以我会选择写旧的语法都是我自己的代码,当我有这样的选择
扎克·史密斯

Answers:


120

新的class语法是,对于现在,大多是语法糖。(但是,您知道,这是很好的糖。)ES2015-ES2020中class没有什么可以构造函数无法做到的,并且Reflect.construct(包括子类ErrorArray¹)也无法做到。(这很可能会有一些东西ES2021,您可以用做class,你不能这样做,否则:私人领域私有方法静态字段/私有静态方法。)

而且,是class另一种OOP还是JavaScript的原型继承?

这与我们一直具有的原型继承相同,只是如果您喜欢使用构造函数(new Foo等等),则使用更简洁,更方便的语法。(特别是在源自Array或的情况下Error,这是您在ES5和更早版本中无法做到的。现在可以使用Reflect.construct[ specMDN ],但不能使用旧的ES5样式。)

我可以使用修改它.prototype吗?

是的,prototype创建类后,仍然可以在类的构造函数上修改对象。例如,这是完全合法的:

class Foo {
    constructor(name) {
        this.name = name;
    }
    
    test1() {
        console.log("test1: name = " + this.name);
    }
}
Foo.prototype.test2 = function() {
    console.log("test2: name = " + this.name);
};

有速度好处吗?

通过提供这种特定的成语,我想这是可能的发动机也许能做得更好优化。但是他们已经非常擅长优化,我希望不会有太大的不同。

ES2015(ES6)class语法有什么好处?

简要地说:如果您一开始不使用构造函数,那么首选Object.create或类似的构造函数class对您没有用。

如果确实使用构造函数,则有一些好处class

  • 语法更简单,更不易出错。

  • 这是很多容易(再次,不易出错)使用新的语法比旧的设置继承层次。

  • class保护您免受未能new与构造函数一起使用的常见错误(如果this不是有效的构造函数,则使构造函数抛出异常)。

  • 使用新语法调用父原型方法的版本比使用旧语法(super.method()而不是ParentConstructor.prototype.method.call(this)or Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this))要简单得多。

这是层次结构的语法比较:

// ***ES2015+**
class Person {
    constructor(first, last) {
        this.first = first;
        this.last = last;
    }

    personMethod() {
        // ...
    }
}

class Employee extends Person {
    constructor(first, last, position) {
        super(first, last);
        this.position = position;
    }

    employeeMethod() {
        // ...
    }
}

class Manager extends Employee {
    constructor(first, last, position, department) {
        super(first, last, position);
        this.department = department;
    }

    personMethod() {
        const result = super.personMethod();
        // ...use `result` for something...
        return result;
    }

    managerMethod() {
        // ...
    }
}

例:

// **ES5**
var Person = function(first, last) {
    if (!(this instanceof Person)) {
        throw new Error("Person is a constructor function, use new with it");
    }
    this.first = first;
    this.last = last;
};

Person.prototype.personMethod = function() {
    // ...
};

var Employee = function(first, last, position) {
    if (!(this instanceof Employee)) {
        throw new Error("Employee is a constructor function, use new with it");
    }
    Person.call(this, first, last);
    this.position = position;
};
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.employeeMethod = function() {
    // ...
};

var Manager = function(first, last, position, department) {
    if (!(this instanceof Manager)) {
        throw new Error("Manager is a constructor function, use new with it");
    }
    Employee.call(this, first, last, position);
    this.department = department;
};
Manager.prototype = Object.create(Employee.prototype);
Manager.prototype.constructor = Manager;
Manager.prototype.personMethod = function() {
    var result = Employee.prototype.personMethod.call(this);
    // ...use `result` for something...
    return result;
};
Manager.prototype.managerMethod = function() {
    // ...
};

现场示例:

如您所见,那里有很多重复和冗长的内容,很容易出错,而且很无聊(这就是为什么我回想起今天编写脚本来这样做的原因)的原因。


¹“ ES2015-ES2018中没有什么class可以做的,而构造函数不能做到,并且Reflect.construct(包括子类ErrorArray)”

例:


9
我不知道这是一个公平的比较。您可以在没有所有麻烦的情况下实现相似的目标。它使用JS通过原型链链接对象的方式,而不是近似经典继承。我做了一个要点,展示了一个基于您的示例的简单示例,以演示如何执行此操作。
杰森·库斯特

8
@JasonCust:是的,这是一个公平的比较,因为我正在展示ES5进行ES6处理的方式class。JavaScript足够灵活,您可以在不需要时使用构造函数,这是您正在做的事情,但这与-唯一的区别是简化了JavaScript中的伪经典继承。classclass
TJ Crowder

3
@JasonCust:是的,这是另一种方式。我不会称其为“更原生”(我知道Kyle确实如此,但这就是Kyle)。构造函数是JavaScript的基础,请查看所有内置类型(除了以某种方式设法Symbol弄乱了反构造派)。但是,最棒的是,如果您不想使用它们,则不必这样做。在我的工作中,我发现构造函数在许多地方都是有意义的,并且语义可以new提高清晰度。并且Object.create在其他许多地方都有意义,尤其是 立面情况。它们是互补的。:-)
TJ Crowder 2015年

4
嗯 我不需要这个。我离开了C#,离开了oop,现在oop正在使用JavaScript。我不喜欢类型。一切都应该是var / object / function。而已。没有更多无关紧要的关键字。继承是一个骗局。如果您想看到类型与非类型之间的良好对抗。看一下肥皂vs休息。而且我们都知道是谁赢了。
foreyez

3
@foreyez:class语法使JavaScript的类型没有比以前更多。就像我在回答中所说的,对于已经存在的内容,它基本上只是(非常)简单的语法。绝对有必要,人们一直在错误地理解旧语法。这也并不意味着您不必再使用继承了。(当然,如果您从不使用旧语法设置“类”,则也无需使用新语法。)
TJ Crowder

22

ES6类是我们今天使用的原型类系统的语法糖。它们使您的代码更加简洁和具有自说明性,这足以使用它们(我认为)。

使用Babel转换此ES6类:

class Foo {
  constructor(bar) {
    this._bar = bar;
  }

  getBar() {
    return this._bar;
  }
}

会给你类似的东西:

var Foo = (function () {
  function Foo(bar) {    
    this._bar = bar;
  }

  Foo.prototype.getBar = function () {
    return this._bar;
  }

  return Foo;
})();

第二个版本并不复杂,需要维护的代码更多。当涉及到继承时,这些模式将变得更加复杂。

因为这些类可以编译为我们一直在使用的相同原型模式,所以您可以对它们进行相同的原型操作。这包括在运行时添加方法等,访问上的方法Foo.prototype.getBar等。

尽管基于ES6的隐私不会导出您不希望访问的对象,但它在今天已经对隐私提供了一些基本支持。例如,您可以:

const BAR_NAME = 'bar';

export default class Foo {
  static get name() {
    return BAR_NAME;
  }
}

并且BAR_NAME其他模块无法直接参考。

许多库都试图支持或解决此问题,例如Backbone的extends助手使用未经验证的类似于方法的函数和属性的哈希值,但是没有公开暴露原型的继承的原型系统。

随着JS代码变得越来越复杂和代码库越来越大,我们已经开始发展许多模式来处理诸如继承和模块之类的事情。用于为模块创建私有作用域的IIFE具有很多花括号和括号。缺少其中一个会导致有效脚本执行完全不同的操作(在模块可以将下一个模块作为参数传递给模块后跳过分号,这很少有好处)。

tl; dr:这是我们已经做的事情的糖,它使您的意图在代码中清晰可见。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.