我有很多关于 ES6 类的问题。
使用class
语法有什么好处?我读到 public/private/static 将成为 ES7 的一部分,这是一个原因吗?
此外,是class
另一种 OOP 还是它仍然是 JavaScript 的原型继承?我可以修改它使用.prototype
吗?或者它只是同一个对象,但有两种不同的声明方式。
有速度优势吗?如果你有一个像大应用程序这样的大应用程序,也许更容易维护/理解?
我有很多关于 ES6 类的问题。
使用class
语法有什么好处?我读到 public/private/static 将成为 ES7 的一部分,这是一个原因吗?
此外,是class
另一种 OOP 还是它仍然是 JavaScript 的原型继承?我可以修改它使用.prototype
吗?或者它只是同一个对象,但有两种不同的声明方式。
有速度优势吗?如果你有一个像大应用程序这样的大应用程序,也许更容易维护/理解?
新的class
语法是,对于现在,大多是语法糖。(但是,你知道,这是一种很好的糖。)在 ES2015-ES2020 中class
没有什么是你不能用构造函数和Reflect.construct
(包括子类化Error
和Array
¹)做的。(这是很可能会有一些东西ES2021,您可以用做class
,你不能这样做,否则:私人领域,私有方法和静态字段/私有静态方法。)
此外,是
class
另一种 OOP 还是它仍然是 JavaScript 的原型继承?
它与我们一直拥有的原型继承相同,只是如果您喜欢使用构造函数(new Foo
等),可以使用更简洁、更方便的语法。(特别是在从Array
or派生的情况下Error
,您在 ES5 及更早版本中无法做到这一点。您现在可以使用Reflect.construct
[ spec , MDN ],但不能使用旧的 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
(包括子类化Error
和Array
)做的”
例子:
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
不会被其他module直接引用。
许多库都试图支持或解决这个问题,比如 Backbone 和他们的extends
助手,它采用未经验证的方法类函数和属性的散列,但没有用于暴露原型继承的一致系统,不涉及与原型混淆。
随着 JS 代码变得越来越复杂,代码库越来越大,我们已经开始进化出很多模式来处理继承和module等问题。用于为module创建私有作用域的 IIFE 有很多大括号和括号;缺少其中一个可能会导致有效脚本执行完全不同的操作(在module可以将下一个module作为参数传递给它之后跳过分号,这很少有好处)。
tl; dr:这是我们已经做的事情的糖,并在代码中明确您的意图。