Javascript ES2015 (ES6) `class` 语法有什么好处?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30783217/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What benefits does ES2015 (ES6) `class` syntax provide?
提问by ssbb
I have many question about ES6 classes.
我有很多关于 ES6 类的问题。
What's the benefit of using classsyntax? I read that public/private/static will be part of ES7, is that a reason?
使用class语法有什么好处?我读到 public/private/static 将成为 ES7 的一部分,这是一个原因吗?
Moreover, is classa different kind of OOP or it still JavaScript's prototypical inheritance? Can I modify it using .prototype? Or is it just the same object but two different ways to declare it.
此外,是class另一种 OOP 还是它仍然是 JavaScript 的原型继承?我可以修改它使用.prototype吗?或者它只是同一个对象,但有两种不同的声明方式。
Are there speed benefits? Maybe it's easier to maintain/understand if you have a big application like big app?
有速度优势吗?如果你有一个像大应用程序这样的大应用程序,也许更容易维护/理解?
回答by T.J. Crowder
The new classsyntax is, for now, mostly syntactic sugar. (But, you know, the goodkind of sugar.) There's nothing in ES2015-ES2019 that classcan do that you can't do with constructor functions and Reflect.construct(including subclassing Errorand Array1). (There islikely to be some things in ES2021 that you can do with classthat you can't do otherwise: private fields, private methods, and static fields/private static methods.)
新的class语法是,对于现在,大多是语法糖。(但是,你知道,这是一种很好的糖。)在 ES2015-ES2019 中class没有什么是你不能用构造函数和Reflect.construct(包括子类化Error和Array1)做的。(有是有可能在ES2021一些东西,你可以做class,你不能这样做,否则:私人领域,私有方法和静态字段/私有静态方法。)
Moreover, is
classa 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 ifyou like using constructor functions (new Foo, etc.). (Particularly in the case of deriving from Arrayor 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.)
它与我们一直拥有的原型继承相同,只是如果您喜欢使用构造函数(new Foo等),则语法更简洁、更方便。(特别是在从Arrayor派生的情况下Error,您在 ES5 及更早版本中无法做到这一点。您现在可以使用Reflect.construct[ spec, MDN],但不能使用旧的 ES5 样式。)
Can I modify it using
.prototype?
我可以修改它使用
.prototype吗?
Yes, you can still modify the prototypeobject on the class's constructor once you've created the class. E.g., this is perfectly legal:
是的,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);
};
Are there speed benefits?
有速度优势吗?
By providing a specific idiom for this, I suppose it's possiblethat 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)
classsyntax provide?
ES2015 (ES6)
class语法有什么好处?
Briefly: If you don't use constructor functions in the first place, preferring Object.createor similar, classisn'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 mucheasier (and again, less error-prone) to set up inheritance hierarchies using the new syntax than with the old.
classdefends you from the common error of failing to usenewwith the constructor function (by having the constructor throw an exception ifthisisn't a valid object for the constructor).Calling the parent prototype's version of a method is much simpler with the new syntax than the old (
super.method()instead ofParentConstructor.prototype.method.call(this)orObject.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this)).
语法更简单,更不容易出错。
这是很多容易(再次,不易出错)使用新的语法比旧的设置继承层次。
class保护您免受未能new与构造函数一起使用的常见错误(通过让构造函数在不是构造函数this的有效对象时抛出异常)。使用新语法调用方法的父原型版本比旧语法(
super.method()而不是ParentConstructor.prototype.method.call(this)orObject.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).
正如你所看到的,那里有很多重复和冗长的东西,很容易出错并且重新输入很无聊(这就是为什么我写了一个脚本来做到这一点,回到当天)。
1 "There's nothing in ES2015-ES2018 that classcan do that you can't do with constructor functions and Reflect.construct(including subclassing Errorand Array)"
1 “在 ES2015-ES2018 中class没有什么是你不能用构造函数和Reflect.construct(包括子类化Error和Array)做的”
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;
}
回答by ssube
ES6 classes are syntactic sugar for the prototypical class system we use today. They make your code more concise and self-documenting, which is reason enough to use them (in my opinion).
ES6 类是我们今天使用的原型类系统的语法糖。它们使您的代码更加简洁和自我记录,这是使用它们的充分理由(在我看来)。
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. For example, you can:
今天在 ES6 中有一些对隐私的基本支持,尽管它基于不导出您不想访问的对象。例如,您可以:
const BAR_NAME = 'bar';
export default class Foo {
static get name() {
return BAR_NAME;
}
}
and BAR_NAMEwill 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 extendshelper 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. The IIFE used to create a private scope for modules has a lot of braces and parens; 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).
随着 JS 代码变得越来越复杂,代码库越来越大,我们已经开始演化出很多模式来处理继承和模块等问题。用于为模块创建私有作用域的 IIFE 有很多大括号和括号;缺少其中一个可能会导致有效脚本执行完全不同的操作(在模块可以将下一个模块作为参数传递给它之后跳过分号,这很少是好的)。
tl;dr: it's sugar for what we already do and makes your intent clear in code.
tl; dr:这是我们已经做的事情的糖,并在代码中明确您的意图。

