Які переваги надає синтаксис класу ES2015 (ES6)?


87

У мене є багато питань щодо класів ES6.

У чому перевага використання classсинтаксису? Я читав, що публічний / приватний / статичний буде частиною ES7, це причина?

Більше того, це classінший тип ООП, чи це все-таки прототипове успадкування JavaScript? Чи можу я змінити його за допомогою .prototype? Або це просто той самий об’єкт, але два різні способи його оголошення.

Чи є переваги в швидкості? Може бути, це простіше підтримувати / розуміти, якщо у вас є великий додаток, як великий додаток?


Тільки моя власна думка: новий синтаксис набагато легший для розуміння і не потребує значного попереднього досвіду (тим більше, що більшість людей походить з мови ООП). Але об’єктну модель прототипу варто знати, і ви ніколи не дізнаєтесь, що, використовуючи новий синтаксис, також буде складніше працювати над кодом прототипу, якщо ви цього вже не знаєте. Тому я вибрав би писати весь власний код зі старим синтаксисом, коли маю такий вибір
Зак Сміт

Відповіді:


120

Новий classсинтаксис, для тепер , в основному синтаксичного цукру. (Але, ви знаєте, хороший сорт цукру.) У ES2015-ES2020 classнемає нічого такого, що не можна робити з функціями конструктора та Reflect.construct(включаючи підкласифікацію Errorта Array¹). (Це є , ймовірно , будуть якісь - то речі в ES2021 , що ви можете зробити з , classщо ви не можете зробити інакше: приватні поля , приватні методи і статичні поля / приватні статичні методи .)

Більше того, це classінший тип ООП, чи це все-таки прототипове успадкування JavaScript?

Це те саме прототипове успадкування, яке ми завжди мали, лише з більш чистим та зручним синтаксисом, якщо вам подобається використовувати функції конструктора ( new Fooтощо). (Зокрема, у випадку походження з Arrayабо Error, чого ви не могли зробити в ES5 і раніше. Тепер ви можете з Reflect.construct[ специфікацією , 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)або 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)"

Приклад:


9
Не знаю, чи це справедливе порівняння. Ви можете досягти подібного завдання, не маючи всього загрози. Він використовує JS-спосіб об'єднання об'єктів через ланцюжки прототипів, а не апроксимує класичне успадкування. Я склав суть, показуючи простий приклад, заснований на вашому, щоб показати, щоб зробити це.
Jason Cust

8
@JasonCust: Так, це справедливе порівняння, тому що я показую ES5 спосіб робити те, що робить ES6 class. JavaScript є досить гнучким, щоб вам не потрібно було використовувати функції конструктора, якщо ви цього не хочете, що саме і робите, але це відрізняється від того class- вся суть у classспрощенні псевдокласичного успадкування в JavaScript.
TJ Crowder,

3
@JasonCust: Так, це інакше. Я б не називав це "більш рідним" (я знаю, що Кайл це робить, але це Кайл). Функції конструктора є фундаментальними для JavaScript, подивіться на всі вбудовані типи (за винятком того, що фракція антиконструктора якось встигла заплутатися Symbol). Але чудова річ, знову ж таки, полягає в тому, що якщо ви не хочете ними користуватися, вам не потрібно. У своїй роботі я виявляю, що функції конструктора мають сенс у багатьох місцях, а семантика newпокращує чіткість; і Object.createмає сенс у багатьох інших місцях, особливо фасадні ситуації. Вони доповнюють один одного. :-)
TJ Crowder

4
Ме Я не купую потреби в цьому. Я відійшов від c #, щоб уникнути oop, і зараз oop повзе на javascript. Я не люблю типи. все має бути var / object / function. Це воно. більше немає сторонніх ключових слів. а спадщина - це шахрайство. якщо ви хочете побачити хорошу боротьбу між типами та нетипами. погляньте на мило проти відпочинку. і всі ми знаємо, хто це виграв.
foreyez

3
@foreyez: classсинтаксис не робить JavaScript більш набраним, ніж був раніше. Як я вже сказав у відповіді, це здебільшого просто (набагато) простіший синтаксис того, що вже було там. У цьому була абсолютно необхідна, люди постійно помилялись у старому синтаксисі. Це також не означає, що вам доведеться користуватися спадщиною більше, ніж раніше. (І звичайно, якщо ви ніколи не налаштовуєте «класи» зі старим синтаксисом, не потрібно цього робити і з новим синтаксисом.)
TJ Crowder,

22

Класи 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: це цукор для того, що ми вже робимо, і чітко вказує ваші наміри в коді.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.