Какие преимущества обеспечивает синтаксис ES2015 (ES6) "класс"?

У меня много вопросов о классах ES6.

Какая польза от использования синтаксиса class? Я читал, что public/private/static будет частью ES7, это причина?

Кроме того, является ли class другим видом ООП или все же прототипическим наследием JavaScript? Могу ли я изменить его с помощью .prototype? Или это просто один и тот же объект, но два разных способа объявить его.

Есть ли преимущества в скорости? Может быть, легче поддерживать/понимать, если у вас есть большое приложение, такое как большое приложение?

Ответ 1

(Почти) полностью зависит от вас, используете ли вы новый синтаксис class. В основном это просто синтаксический сахар. (Но, вы знаете, хороший вид сахара.) В ES2015-ES2018 нет ничего, что class не может сделать, что вы не можете сделать с помощью функций конструктора и Reflect.construct (включая создание подклассов Error и Array ¹), (Скорее всего, is будет чем-то в ES2019 или ES2020, что вы можете сделать с class, что вы не можете сделать иначе: частные поля и частные методы.)

Кроме того, class - это другой тип ООП или это все еще прототипное наследие JavaScript?

Это то же самое прототипическое наследование, которое мы всегда имели, только с более чистым и более удобным синтаксисом, если вам нравится использовать функции конструктора (new Foo и т.д.). (Особенно в случае получения из Array или 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);
};

Есть ли преимущества в скорости?

Предоставляя конкретную идиому для этого, я полагаю, возможно, что двигатель сможет лучше оптимизировать работу. Но они уже очень хороши в оптимизации, я бы не ожидал существенной разницы.

What benefits does ES2015 (ES6) class syntax provide?

Вкратце: если вы не используете функции конструктора в первую очередь, предпочитая 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());

Ответ 2

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; Отсутствие одного из них может привести к действию script, который делает что-то совершенно другое (пропуская точку с запятой после того, как модуль сможет передать следующий модуль к нему в качестве параметра, что редко бывает хорошим).

tl; dr: это сахар для того, что мы уже делаем, и делает ваше намерение четким в коде.