У меня много вопросов по классам ES6.
Какая польза от использования class
синтаксиса? Я читал, что публичные / частные / статические будут частью ES7, это причина?
Более того, является class
ли ООП другим видом или это все еще прототипное наследование JavaScript? Могу ли я изменить его с помощью .prototype
? Или это один и тот же объект, но двумя разными способами его объявить.
Есть ли преимущества в скорости? Может быть, проще поддерживать / понимать, если у вас есть большое приложение, такое как большое приложение?
Ответы:
Новый
class
синтаксис, для теперь , в основном синтаксического сахара. (Но, знаете, хороший вид сахара.) В ES2015-ES2020class
нет ничего такого, что нельзя было бы сделать с функциями конструктора иReflect.construct
(включая подклассыError
иArray
¹). (Это является , вероятно , будут какие - то вещи в ES2021 , что вы можете сделать с ,class
что вы не можете сделать иначе: частные поля , частные методы и статические поля / частные статические методы .)Это то же прототипное наследование, которое у нас всегда было, только с более чистым и удобным синтаксисом, если вам нравится использовать функции конструктора (
new Foo
и т. Д.). (В особенности в случае наследования отArray
илиError
, чего нельзя было сделать в ES5 и ранее. Теперь вы можете использоватьReflect.construct
[ spec , MDN ], но не в старом стиле ES5.)Да, вы все равно можете изменить
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); };
Предоставляя для этого определенную идиому, я полагаю, что возможно, что движок сможет лучше выполнять оптимизацию. Но они уже ужасно хороши в оптимизации, я бы не ожидал значительной разницы.
Вкратце: если вы в первую очередь не используете функции-конструкторы, предпочитаемые
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());
Как видите, там много повторяющихся и многословных вещей, в которых легко ошибиться и скучно перепечатывать (именно поэтому я написал сценарий для этого в свое время).
¹ "В 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; }
источник
class
. JavaScript достаточно гибок, поэтому вам не нужно использовать функции конструктора, если вы этого не хотите, что вы делаете, но это отличается отclass
- весь смысл вclass
упрощении псевдоклассического наследования в JavaScript.Symbol
). Но, опять же, самое замечательное в том, что если вы не хотите их использовать, вам и не нужно. В своей работе я обнаружил, что функции-конструкторы имеют смысл во многих местах, а семантикаnew
улучшает ясность; иObject.create
имеет смысл во многих других местах, особенно. фасадные ситуации. Они дополняют друг друга. :-)class
синтаксис не делает JavaScript более типизированным, чем это было раньше. Как я сказал в ответе, это в основном (намного) более простой синтаксис для того, что уже было. В этом была абсолютная необходимость, люди постоянно ошибались в старом синтаксисе. Это также не означает, что вам нужно использовать наследование больше, чем раньше. (И, конечно же, если вы никогда не настраивали «классы» со старым синтаксисом, нет необходимости делать это и с новым синтаксисом.)Классы 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: это сахар для того, что мы уже делаем, и проясняет ваши намерения в коде.
источник