简体   繁体   English

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

[英]What benefits does ES2015 (ES6) `class` syntax provide?

I have many question about ES6 classes.我对 ES6 类有很多疑问。

What's the benefit of using class syntax?使用class语法有什么好处? I read that public/private/static will be part of ES7, is that a reason?我读到 public/private/static 将成为 ES7 的一部分,这是一个原因吗?

Moreover, is class a different kind of OOP or it still JavaScript's prototypical inheritance?此外, class是不同类型的 OOP 还是 JavaScript 的原型 inheritance? Can I modify it using .prototype ?我可以使用.prototype修改它吗? Or is it just the same object but two different ways to declare it.或者它只是相同的 object 但有两种不同的声明方式。

Are there speed benefits?有速度优势吗? Maybe it's easier to maintain/understand if you have a big application like big app?如果您有像大型应用程序这样的大型应用程序,也许更容易维护/理解?

The new class syntax is, for now , mostly syntactic sugar.class的语法是,就目前而言,主要是语法糖。 (But, you know, the good kind of sugar.) There's nothing in ES2015-ES2020 that class can do that you can't do with constructor functions and Reflect.construct (including subclassing Error and Array ¹). (但是,你知道,这是一种很好的糖。)在 ES2015-ES2020 中,没有什么是class可以做的,而你不能用构造函数和Reflect.construct (包括子类化ErrorArray ¹)。 (It is likely there will be some things in ES2021 that you can do with class that you can't do otherwise: private fields , private methods , and static fields/private static methods .) (很可能会出现在ES2021一些东西,你可以用做class ,你不能这样做,否则:私人领域私有方法静态字段/私有静态方法。)

Moreover, is class a different kind of OOP or it still JavaScript's prototypical inheritance?此外, class是另一种 OOP 还是 JavaScript 的原型继承?

It's the same prototypical inheritance we've always had, just with cleaner and more convenient syntax if you like using constructor functions ( new Foo , etc.).它与我们一直拥有的原型继承相同,只是如果您喜欢使用构造函数( new Foo等),则使用更简洁、更方便的语法。 (Particularly in the case of deriving from Array or Error , which you couldn't do in ES5 and earlier. You can now with Reflect.construct [ spec , MDN ], but not with the old ES5-style.) (特别是在从ArrayError派生的情况下,您在 ES5 及更早版本中无法做到这一点。您现在可以使用Reflect.construct [ spec , MDN ],但不能使用旧的 ES5 样式。)

Can I modify it using .prototype ?我可以使用.prototype修改它吗?

Yes, you can still modify the prototype object on the class's constructor once you've created the class.是的,一旦创建了类,您仍然可以在类的构造函数上修改prototype对象。 Eg, this is perfectly legal:例如,这是完全合法的:

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

Are there speed benefits?有速度优势吗?

By providing a specific idiom for this, I suppose it's possible that the engine may be able to do a better job optimizing.通过提供这种特定的成语,我想这可能是发动机也许能做得更好优化。 But they're awfully good at optimizing already, I wouldn't expect a significant difference.但是他们已经非常擅长优化,我不希望有显着差异。

What benefits does ES2015 (ES6) class syntax provide? ES2015 (ES6) class语法提供了什么好处?

Briefly: If you don't use constructor functions in the first place, preferring Object.create or similar, class isn't useful to you.简而言之:如果您一开始不使用构造函数,则更喜欢Object.create或类似的, class对您没有用。

If you do use constructor functions, there are some benefits to class :如果您确实使用构造函数,则class有一些好处:

  • The syntax is simpler and less error-prone.语法更简单,更不容易出错。

  • It's much easier (and again, less error-prone) to set up inheritance hierarchies using the new syntax than with the old.与旧语法相比,使用新语法设置继承层次结构容易得多(而且更不容易出错)。

  • class defends you from the common error of failing to use new with the constructor function (by having the constructor throw an exception if this isn't a valid object for the constructor). class保护您免受未能将new与构造函数一起使用的常见错误(如果this不是构造函数的有效对象,则让构造函数抛出异常)。

  • Calling the parent prototype's version of a method is much simpler with the new syntax than the old ( super.method() instead of ParentConstructor.prototype.method.call(this) or Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this) ).使用新语法调用方法的父原型版本比旧语法( super.method()而不是ParentConstructor.prototype.method.call(this)Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this)简单得多Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this) )。

Here's a syntax comparison for a hierarchy:这是层次结构的语法比较:

// ***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() {
        // ...
    }
}

Example:例子:

 // ***ES2015+** class Person { constructor(first, last) { this.first = first; this.last = last; } personMethod() { return `Result from personMethod: this.first = ${this.first}, this.last = ${this.last}`; } } class Employee extends Person { constructor(first, last, position) { super(first, last); this.position = position; } personMethod() { const result = super.personMethod(); return result + `, this.position = ${this.position}`; } employeeMethod() { // ... } } class Manager extends Employee { constructor(first, last, position, department) { super(first, last, position); this.department = department; } personMethod() { const result = super.personMethod(); return result + `, this.department = ${this.department}`; } managerMethod() { // ... } } const m = new Manager("Joe", "Bloggs", "Special Projects Manager", "Covert Ops"); console.log(m.personMethod());

vs.对比

// **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() {
    // ...
};

Live Example:现场示例:

 // **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() { return "Result from personMethod: this.first = " + this.first + ", this.last = " + this.last; }; 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.personMethod = function() { var result = Person.prototype.personMethod.call(this); return result + ", this.position = " + this.position; }; 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); return result + ", this.department = " + this.department; }; Manager.prototype.managerMethod = function() { // ... }; var m = new Manager("Joe", "Bloggs", "Special Projects Manager", "Covert Ops"); console.log(m.personMethod());

As you can see, lots of repeated and verbose stuff there which is easy to get wrong and boring to retype (which is why I wrote a script to do it , back in the day).正如你所看到的,那里有很多重复和冗长的东西,很容易出错并且重新输入很无聊(这就是为什么我写了一个脚本来做到这一点,回到当天)。


¹ "There's nothing in ES2015-ES2018 that class can do that you can't do with constructor functions and Reflect.construct (including subclassing Error and Array )" ¹“在 ES2015-ES2018 中,没有什么是class可以做的,而你不能用构造函数和Reflect.construct (包括子类化ErrorArray )”

Example:例子:

 // Creating an Error subclass: function MyError(...args) { return Reflect.construct(Error, args, this.constructor); } MyError.prototype = Object.create(Error.prototype); MyError.prototype.constructor = MyError; MyError.prototype.myMethod = function() { console.log(this.message); }; // Example use: function outer() { function inner() { const e = new MyError("foo"); console.log("Callng e.myMethod():"); e.myMethod(); console.log(`e instanceof MyError? ${e instanceof MyError}`); console.log(`e instanceof Error? ${e instanceof Error}`); throw e; } inner(); } outer();
 .as-console-wrapper { max-height: 100% !important; }

ES6 classes are syntactic sugar for the prototypical class system we use today. ES6 类是我们今天使用的原型类系统的语法糖。 They make your code more concise and self-documenting, which is reason enough to use them (in my opinion).它们使您的代码更加简洁和自我记录,这是使用它们的充分理由(在我看来)。

Using Babel to transpile this ES6 class:使用 Babel 转译这个 ES6 类:

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

  getBar() {
    return this._bar;
  }
}

will give you something like:会给你类似的东西:

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

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

  return Foo;
})();

The second version isn't much more complicated, it is more code to maintain.第二个版本并不复杂,需要维护的代码更多。 When you get inheritance involved, those patterns become even more complicated.当涉及到继承时,这些模式会变得更加复杂。

Because the classes compile down to the same prototypical patterns we've been using, you can do the same prototype manipulation on them.因为这些类编译成我们一直在使用的相同原型模式,所以您可以对它们进行相同的原型操作。 That includes adding methods and the like at runtime, accessing methods on Foo.prototype.getBar , etc.这包括在运行时添加方法等,访问Foo.prototype.getBar方法等。

There is some basic support for privacy in ES6 today, although it's based on not exporting the objects you don't want accessible.今天在 ES6 中有一些对隐私的基本支持,尽管它基于不导出您不想访问的对象。 For example, you can:例如,您可以:

const BAR_NAME = 'bar';

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

and BAR_NAME will not be available for other modules to reference directly.并且BAR_NAME将不可用于其他模块直接引用。

A lot of libraries have tried to support or solve this, like Backbone with their extends helper that takes an unvalidated hash of method-like functions and properties, but there's no consist system for exposing prototypical inheritance that doesn't involve mucking around with the prototype.许多库都试图支持或解决这个问题,比如 Backbone 和他们的extends助手,它采用未经验证的方法类函数和属性的散列,但没有用于暴露原型继承的一致性系统,不涉及与原型混杂.

As JS code becomes more complicated and codebases larger, we've started to evolve a lot of patterns to handle things like inheritance and modules.随着 JS 代码变得越来越复杂,代码库越来越大,我们已经开始演化出很多模式来处理继承和模块等问题。 The IIFE used to create a private scope for modules has a lot of braces and parens;用于为模块创建私有作用域的 IIFE 有很多大括号和括号; missing one of those can result in a valid script that does something entirely different (skipping the semicolon after a module can pass the next module to it as a parameter, which is rarely good).缺少其中一个可能会导致有效脚本执行完全不同的操作(在模块可以将下一个模块作为参数传递给它之后跳过分号,这很少是好的)。

tl;dr: it's sugar for what we already do and makes your intent clear in code. tl; dr:这是我们已经做的事情的糖,并在代码中明确您的意图。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM