Цікаві пропозиції ECMAScript 2017, які ще не були прийняті

16 хв. читання

Важко постійно мати оновлену інформацію щодо всіх нових пропозицій функцій. Щороку в комітет TC39, який контролює розвиток JavaScript, пропонуються десятки функцій. Оскільки багато з них ніколи не дійдуть до другої стадії, важко визначити, за якими з них слід стежити, а які є лише новинками (або просто божевільними ідеями).

Однією з причин, через які може бути важко залишатись на інформаційній вершині пропозицій по функціях, — те, що зараз їх набагато більше. Темпи розробки JavaScript раніше були набагато консервативнішими — між ES5 і ES6 минуло шість років. З виходом ECMAScript 2016 (ES7), процес став значно більше стандартизованим і вимагає щорічних релізів.

Оскільки поліфіли (polyfills) і транспілери (transpilers) стали популярними в останні роки, деякі пропозиції на ранній стадії отримали значне признання, перш ніж вони навіть були завершені. І оскільки пропозиції можуть значно змінюватися, перш ніж будуть прийняті, деякі можуть виявити, що вони використовують функцію, яка ніколи не буде частиною мови.

Перш, ніж говорити про пропозиції, виділимо спочатку хвильку на ознайомлення з поточним процесом.

П'ять стадій процесу пропозицій ECMAScript

Стадія 0 «strawman» — відправна точка для всіх пропозицій. Вони можуть значно змінюватись, перш, ніж перейти на наступну стадію. На цій стадії нема ніяких критеріїв прийняття, тому кожен може зробити нову пропозицію. Немає також потреби ні в якій реалізації, а специфікація не дотримується жодного стандарту. Ця стадія призначена для початку обговорення цієї функції. На даний час існує понад 20 пропозицій на цій стадії.

Стадія 1 «proposal» — фактична офіційна пропозиція. Для цього потрібен «чемпіон» (тобто член комітету TC39). На цій стадії API вже має бути добре продуманим, а будь-які потенційні проблеми реалізації повинні бути викладені. Також на цій стадії розробляється поліфіл та створюються демо. Після неї можуть відбутися серйозні зміни, тому використовуйте з обережністю. Пропозиції на цій стадії включають довгоочікуваний тип observables та функцію Promise.try.

Стадія 2 «draft» — на цій стадії синтаксис точно описується за допомогою формальної мови специфікації TC39. Після цієї стадії все ще можуть відбутися невеликі редакційні зміни, але специфікація повинна бути достатньо повною, щоб не вимагати серйозних ревізій. Якщо пропозиція доходить так далеко, то це гарний знак, який ознаменує, що комітет розраховує на те, що функція зрештою буде включена.

Стадія 3 «candidate» — пропозиція схвалена, і подальші зміни відбудуться тільки за запитом авторів реалізації. Ось де ви можете розраховувати на те, що нововведення почнуть реалізовувати на рушіях JavaScript. Поліфіли для пропозицій на цій стадії безпечні для використання без проблем.

Стадія 4 «finished» — вказує, що пропозиція була прийнята, а специфікація — об'єднана з основною специфікацією JavaScript. Ніяких подальших змін не очікується. Очікується, що рушії JavaScript представлять свої реалізації. Станом на жовтень 2017, є 9 готових пропозицій, в першу чергу — асинхронних функцій.

Оскільки пропозицій так багато, ось деякі з найбільш цікавих серед тих, які зараз розглядаються.

Асинхронна ітерація

ECMAScript 2015 додав ітератори, включаючи синтаксис циклу for-of. Це значно спростило перебір ітерабельних об'єктів та дозволило реалізувати свої власні ітерабельні структури даних.

На жаль, ітератори не можуть бути використані для представлення асинхронних структур даних, таких як доступ до файлової системи. Хоча ви завжди могли запустити Promise.all і перебрати масив promises, але це потребувало синхронного визначення стану «done».

Наприклад, замість того, щоб зчитувати всі рядки файлу перед роботою з ними, з асинхронними ітераторами ви можете просто перебирати асинхронно ітеровані об'єкти й зчитувати рядки тільки тоді, коли вони вам потрібні.

Ви можете визначити функцію-генератор async просто використовуючи синтаксис генератора та синтаксис async-wait разом:

async function* readLines(path) {
  let file = await fileOpen(path);

  try {
    while (!file.EOF) {
      yield await file.readLine();
    }
  } finally {
    await file.close();
  }
}

Приклад

Потім ви можете використовувати цей асинхронний генератор у циклі for-await-of:

for await (const line of readLines(filePath)) {
  console.log(line);
}

Будь-який об'єкт, який має властивість Symbol.asyncIterator, визначається як асинхронний ітерабельний та може використовуватись разом з новим синтаксисом for-await-of. Ось приклад цього в дії:

class LineReader() {
 constructor(filepath) {
   this.filepath = filepath;
   this.file = fileOpen(filepath);
 }

 [Symbol.asyncIterator]: {
   next() {
     return new Promise((resolve, reject) => {
       if (this.file.EOF) {
         resolve({ value: null, done: true });
       } else {
         this.file.readLine()
           .then(value => resolve({ value, done: false }))
           .catch(error => reject(error));
       }
     });
   }
 }
}

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

На даний час проект regenerator має базову підтримку для цієї пропозиції асинхронного генератора. Однак, сам regenerator не підтримує синтаксис циклу for-await-of. Компілятор Babel має плагін transform-async-generator-functions, який підтримує як функції асинхронного генератора, так і синтаксис циклу for-await-of.

Покращення класів

Є пропозиція додати публічні й приватні поля та приватні методи до синтаксису класів, які були введенні в ECMAScript 2015. Ця пропозиція — кульмінація тривалого періоду дискусії й конкуренції між різними конкурентними пропозиціями.

Використання приватних полів та методів аналогічно до публічних, але з їх іменами, доданими за допомогою символу хешу. Будь-який метод або поле, помічені як приватні, не будуть видимими поза класом, гарантуючи сувору інкапсуляцію внутрішніх членів класу.

Ось приклад гіпотетичного React-подібного компонента, який використовує публічні та приватні поля разом з приватним методом:

class Counter {
  // public field
  text = 'Counter';

  // private поле 
  #state = {
    count: 0,
  };

  // private метод 
  #handleClick() {
    this.#state.count++;
  }

  // public метод 
  render() {
    return (
      <button onClick={this.handleClick.bind(this)}>
        {this.text}: {this.#state.count.toString()}
      </button>
    );
  }
}

Приватні поля та методи класу наразі не поліфійовані (polyfilled) у Babel, хоча скоро це відбудеться. Публічні поля підтримуються плагіном Babel transform-class-properties, однак підтримка основана на більш ранній пропозиції, яка була об'єднана у цю уніфіковану пропозицію публічних/приватних полів класу. Ця пропозиція досягла третьої стадії 1 вересня 2017 року, тому буде безпечно використовувати будь-які поліфіли, коли вони стануть доступні.

Декоратори класів

Декоратори — гарний приклад пропозиції, яка змінилася повністю після того, як була представлена. В Babel v5 реалізована оригінальна специфікація декораторів другої стадії, яка визначає декоратор як функцію, що приймає дескриптор цілі, імені та властивості. Сьогодні найпопулярнішим способом трансляції декораторів є плагін Babel transform-legacy-decorators, який реалізує цю стару специфікацію.

Нова специфікація зовсім інша. Замість функції з трьома властивостями, тепер ми маємо формалізований опис члену класу — декоратори, які є функціями, що змінюють цей дескриптор. Цей новий «дескриптор члену класу» дуже схожий на інтерфейс дескриптора властивостей, введений в ES5.

Наразі існує два різних видів декораторів з різними API: декоратори членів класу та декоратори класів.

  • Декоратори членів класу приймають дескриптор члену та повертають дескриптор члену.
  • Декоратори класів приймають конструктор, клас предка та масив дескрипторів членів для кожного члену класу (тобто кожну властивість чи метод).

В «додатках» специфікації говориться про необов'язковий масив дескрипторів членів класу, який може бути доданий за допомогою декоратора. Це дозволить декоратору динамічно створювати нові властивості та методи. Наприклад, у вас може бути декоратор, який створює функції getter і setter для властивості.

Подібно до старої специфікації, нова дозволяє вам змінювати дескриптори властивостей членів класу. Крім того, декоратори досі допускаються до властивостей об'єктних літералів.

Ймовірно, що специфікація значно зміниться, перш ніж вона буде завершена. У синтаксисі є неоднозначності, і багато проблемних моментів старої специфікації ще не були розглянуті. Декоратори — величезне розширення синтаксису для мови, тому цю затримку слід було очікувати. На жаль, якщо нова пропозиція буде прийнята, вам доведеться провести рефакторинг ваших функцій декоратора для роботи з новим інтерфейсом.

Багато авторів бібліотек надають перевагу продовженню підтримки старої пропозиції та оброблених за допомогою Babel легасі-декораторів — навіть не дивлячись на те, що нова пропозиція вже на другій стадії, а стара все ще на нульовій. Найпопулярніша JavaScript бібліотека з відкритим вихідним кодом, яка використовує декоратори, core-decorators, застосувала цей підхід. Ймовірно, автори бібліотеки декораторів продовжать підтримувати стару специфікацію протягом багатьох років.

Також існує шанс того, що нова пропозиція буде відкликана на користь іншої, і декоратори, можливо, не потраплять у Javascript в 2018. Після того, як Babel закінчить роботу над новим плагіном обробки, ви зможете використовувати нову пропозицію декораторів.

Функція Import

6-те видання ECMAScript додало оператор імпорту й завершило семантику навколо нової системи модулів. Основні браузери тільки нещодавно випустили підтримку, хоча є незначні відмінності в тому, яка частина специфікації була реалізована. NodeJS у версії 8.5.0 випустила попередню підтримку модулів ECMAScript — за прапором experimental-modules.

Однак, у пропозиції був відсутній асинхронний спосіб імпорту модулів, що ускладнювало динамічний імпорт коду модуля під час виконання. На даний час єдиним способом динамічного завантаження модулів у браузері є динамічна вставка тегу скрипта типу «module» разом з декларацією імпорту в якості його textContent.

Вбудований спосіб це зробити — запропонований синтаксис динамічного імпорту, який викликає форму завантаження «подібного до функції» модуля імпорту. Цей новий динамічний синтаксис імпорту так само буде дозволений у коді модуля, як і звичайний код скрипта, пропонуючи зручну точку входу для коду модуля.

У минулому році була пропозиція вирішити цю проблему, запропонувавши функцію System.import(), але в підсумку ця ідея була остаточно виключена з фінальної специфікації. Нова пропозиція знаходиться на третій стадії, і, скоріш за все, буде включена до кінця року.

Observables

Запропонований тип Observable надає стандартизований спосіб обробки асинхронних потоків даних. Вони вже були реалізовані в тій чи іншій формі в багатьох популярних JavaScript фреймворках, таких як RxJS. Поточна пропозиція значною мірою залежить від цих бібліотек.

Observables створюються за допомогою конструктора Observable, який приймає функцію підписника:

function listen(element, eventName) {
 return new Observable(observer => {
   // Створює обробник подій, який надсилає дані в потік
   let handler = event => observer.next(event);

   // Прикріплює обробник подій
   element.addEventListener(eventName, handler, true);

   // Повертає функцію, яка буде викликана для скасування підписки
   return () => {
     // Від'єднує обробник подій від елемента
     element.removeEventListener(eventName, handler, true);
   };
 });
}

Функція subscribe використовується, щоб підписатися на видавця (observable):

const subscription = listen(inputElement, "keydown").subscribe({
  next(val) { console.log("Got key: " + val) },
  error(err) { console.log("Got an error: " + err) },
  complete() { console.log("Stream complete!") },
});

Функція subscribe() повертає об'єкт підписки. Цей об'єкт має метод unsubscribe, який може бути використаний для скасування підписки.

Observables не слід плутати зі застарілою функцією Object.observe, яка була способом відстеження змін об'єкту. Цей метод був замінений більш загальною реалізацією проксі в ECMAScript 2015.

Observables наразі знаходиться на стадії 1, але він, скоріш за все, найближчим часом перейде на наступну стадію, оскільки він був помічений комітетом TC39 як «готовий до просування» і в нього є сильна підтримка постачальників браузерів. На вибір вже є три варіанти реалізації поліфілів, тому ви можете почати використовувати його вже сьогодні.

Вираз Do

CoffeeScript був добре відомий тим, що робив усе виразами, і хоча популярність coffeescript зменшилася, він вплинув на нещодавню еволюцію JavaScript.

Do-вирази — запропонований новий синтаксис для обгортання кількох операторів у один вираз. Це дозволить вам зробити наступне:

let activeToDos = do {
  let result;
  try {
    result = fetch('/todos');
  } catch (error) {
    result = []
  }
  result.filter(item => item.active);
}

Останній оператор у виразі do неявно повертається як значення завершення.

Do-вирази будуть досить корисними всередині JSX. Замість використання складних тернарних операторів, вираз do зробить потік управління всередині JSX більш читабельним.

const FooComponent = ({ kind }) => (
 <div>
   {do {
     if (kind === 'bar') { <BarComponent /> }
     else if (kind === 'baz') { <BazComponent /> }
     else { <OtherComponent /> }
   }}
 </div>
)

У Babel є плагін для обробки do-виразів. На даний час ця пропозиція знаходиться на першій стадії, і є відкриті серйозні питання щодо того, як do-вирази працюють з генераторами та асинхронними функціями, тому специфікація може значно змінитися.

Необов'язкове ланцюгування

Ще одна пропозиція, натхненна CoffeeScript, — необов'язкове ланцюгування, що додає простий спосіб отримати доступ до властивостей об'єктів, які можуть бути невизначеними або нульовими без довгих тернарних операторів. Це схоже на екзистенціальний оператор CoffeeScript, але з деякими помітними недоліками, такими, як перевірка області видимості та необов'язкове призначення.

Приклад:

a?.b // undefined if `a` is null/undefined, `a.b` otherwise.
a == null ? undefined : a.b // використання тернарних операторів

Ця пропозиція знаходиться на першій стадії, і є плагін Babel, який називається babel-plugin-transform-optional-chaining, що її реалізує. Комітет TC39 на своєму останньому засіданні в жовтні 2017 висловив занепокоєння з приводу синтаксису. Але здається ймовірним, що в кінцевому підсумку пропозиція щодо необов'язкового ланцюгування буде прийнята.

Стандартизований глобальний об'єкт

Важко писати код, який може запускатися в кожному середовищі JavaScript. У браузері глобальний об'єкт — вікно, якщо ви не працюєте у веб воркері, а потім він сам. У NodeJS він глобальний, але це щось додане поверх рушія V8, а не частина специфікації, тому він недоступний при запуску коду безпосередньо на рушії V8.

Існує пропозиція стандартизувати глобальний об'єкт, який буде доступний для всіх рушіїв та середовищ виконання. В даний час ця пропозиція на стадії 3, і тому буде прийнята найближчим часом.

І багато іншого

Наразі комітет TC39 розглядає понад п'ятдесят активних пропозицій, не рахуючи понад двадцять пропозицій на стадії 0, які ще не були розроблені.

Ви можете переглянути список всіх активних пропозицій на сторінці GitHub комітету TC39. Також ви можете знайти деякі з більш чорнових ідей у секції пропозицій стадії 0, яка охоплює такі ідеї, як декоратори параметрів методу та новий синтаксис зіставлення шаблонів.

Також існують репозитарії записів зустрічі та порядку денного з попередніх зустрічей TC39, де ви можете поглянути на пріоритети комітету та проблеми, які зараз розглядаються. Якщо вас це цікавить, презентації також архівуються разом з вузлами зустрічі.

В останніх випусках ECMAScript було кілька основних пропозицій, що змінюють синтаксис, і ця тенденція, схоже, триває. Темпи змін прискорюються — з кожним роком з'являється все більше пропозицій, і виглядає так, що видання 2018 року матиме більше прийнятих пропозицій, ніж видання 2017 року.

У цьому році менше пропозицій щодо покращення «якості життя» мови, як-от пропозиція додати кращу перевірку типу вбудованих об'єктів або пропозиція додати градуси та радіанні помічники до модуля Math. Подібні пропозиції додають до стандартної бібліотеки замість того, щоб змінювати синтаксис. Вони легші для реалізації поліфілів та зменшують потребу встановлювати сторонні бібліотеки. Оскільки вони не змінюють синтаксис, то приймаються швидко і часто витрачають менше часу на процес подання пропозицій.

Сподіваюсь, що ми побачимо більше таких пропозицій у майбутньому. Javascript, як кажуть, не вистачає хорошої стандартної бібліотеки, але зрозуміло, що люди працюють над тим, щоб це змінити.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.8K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація