我对ES6课程有很多疑问。
使用class
语法有什么好处?我读到公共/私有/静态将成为ES7的一部分,这是一个原因吗?
此外,class
是一种不同类型的OOP还是它仍然是JavaScript的原型继承?我可以使用.prototype
修改它吗?或者它只是同一个对象,但有两种不同的方式来声明它。
速度有益吗?如果你有一个像大应用程序这样的大应用程序,可能更容易维护/理解?
答案 0 :(得分:110)
(几乎)完全由你决定。新的class
东西大多只是语法糖。 (但是,你知道,那种好糖。)ES2015-ES2018中没有任何内容class
可以做到你无法用构造函数和Reflect.construct
做的事情(包括子类化Error
和Array
¹)。 ( 可能是ES2019或ES2020中的某些内容,您可以使用class
执行此操作,否则您无法做到:private fields和private methods。)< / p>
此外,
class
是一种不同类型的OOP还是它仍然是JavaScript的原型继承?
它与我们一直拥有的原型继承相同,只是使用更清晰,更方便的语法如果你喜欢使用构造函数(new Foo
等)。 (特别是在派生自Array
或Error
的情况下,您在ES5及更早版本中无法做到。您现在可以使用Reflect.construct
[spec,{{3 },但不是旧的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)
或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() {
// ...
}
}
示例:
// ***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() {
// ...
};
直播示例:
// **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());
正如你所看到的那样,那里很多重复和冗长的东西容易出错并且无法重新打字(这就是为什么我在当天写了MDN)。
¹“ES2015-ES2018中没有任何内容class
无法使用构造函数和Reflect.construct
(包括子类Error
和Array
)”
示例:
// 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;
}
答案 1 :(得分:19)
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用于创建模块的私有范围有很多括号和parens;缺少其中一个可能导致一个完全不同的有效脚本(在模块可以将下一个模块作为参数传递给它之后跳过分号,这很少好)。
tl; dr:这是我们已经做过的事情,并且在代码中明确了你的意图。