ES6 по-людськи

Від перекладача:
Пропоную вашій увазі переклад короткого (дійсно короткого) керівництва з ES6. В ньому можна ознайомитися з основними поняттями стандарту.
Оригінальний текст у деяких випадках був доповнений або змінений на більш підходящий джерело. Наприклад, частина визначення ключового слова
const
є перекладом документації з MDN.

Щоб краще розібратися в деяких концепціях (для виконання якісного перекладу) використовувалося опис стандарту на сайті MDN, керівництво "You don't Know JS: ES6 & Beyond" і підручник Іллі Кантора.
Переклад виклав на Гитхаб: https://github.com/etnolover/ES6-for-humans-translation. У разі знаходження помилок пишіть, виправлю.
Посилання на оригінальний текст: https://github.com/metagrover/ES6-for-humans


Зміст


1. let, const і блокова область видимості
Ключове слово
let
дозволяє оголошувати змінні з обмеженою областю видимості — тільки для блоку {...}, в якому відбувається оголошення. Це називається блокової областю видимості. Замість ключового слова
var
, яке забезпечує область видимості всередині функції, стандарт ES6 рекомендує використовувати
let
.
var a = 2;
{
let a = 3;
console.log(a); // 3
}
console.log(a); // 2

Інший формою оголошення змінної з блочною областю видимості є ключове слово
const
. Воно призначене для оголошення змінних (констант), значення яких доступні тільки для читання. Це означає не те, що значення константи незмінно, а те, що ідентифікатор змінної не може бути переприсвоен.
Ось простий приклад:
{
const ARR = [5, 6];
ARR.push(7);
console.log(ARR); // [5,6,7]
ARR = 10; // TypeError
ARR[0] = 3; // значення можна змінювати
console.log(ARR); // [3,6,7]
}

Про що варто пам'ятати:
  • Коли справа стосується підняття змінних (hoisting)
    let
    та
    const
    , їх поведінка відрізняється від традиційного поведінки
    var
    та
    function
    . І
    let
    та
    const
    не існують до свого оголошення (від перекладача: для подробиць автор оригінального керівництва відсилає до статті Temporal Dead Zone)
  • Областю видимості
    let
    та
    const
    є найближчий блок.
  • При використанні
    const
    рекомендується використовувати ПРОПИСНЫЕ_БУКВЫ.
  • const
    одночасно з оголошенням змінної повинно бути присвоєно значення.


2. Стрілочні функції
Стрілочні функції являють собою скорочений запис функцій в ES6. Стрілкова функція складається з списку параметрів
( ... )
, за яким слідує символ
=>
і тіло функції.
// Класичне функціональний вираз
let addition = function(a, b) {
return a + b;
};

// Стрілкова функція
let addition = (a, b) => a + b;

Зауважимо, що в наведеному вище прикладі, тіло функції являє собою коротку запис, в якій не потрібно явної вказівки на те, що ми хочемо повернути результат.
А ось приклад з використанням блоку з фігурних дужок:
let arr = ['apple', 'banana', 'orange'];

let breakfast = arr.map(fruit => {
return fruit + 's';
});

console.log(breakfast); // ['apples', 'bananas', 'oranges']

Це ще не все!...
Стрілочні функції не просто роблять код коротше. Вони тісно пов'язані з ключовим словом
this
і прив'язкою контексту.
Поведінка стрілочних функцій з ключовим словом
this
відрізняється від поведінки звичайних функцій з
this
. Кожна функція в JavaScript визначає свій власний контекст
this
, але всередині стрілочних функцій значення
this
те ж саме, що і зовні (стрілочні функції не мають свого
this
). Подивимося на наступний код:
function Person() {
// Конструктор Person() визначає `this` як примірник самого себе.
this.age = 0;

setInterval(function growUp() {
// Без використання `use strict`, функція growUp() визначає `this`
// як глобальний об'єкт, який відрізняється від `this`,
// визначеного конструктором Person().
this.age++;
}, 1000);
}
var p = new Person();

В ECMAScript 3/5 це поведінка стало можливим змінити, присвоївши значення
this
іншої змінної.
function Person() {
var self = this;
self.age = 0;

setInterval(function growUp() {
// Коллбек відноситься до змінної `self`,
// значенням якої є очікуваний об'єкт.
self.age++;
}, 1000);
}

Як сказано вище, в межах стрілочних функцій значення
this
те ж саме, що і зовні, тому наступний код працює так, як від нього очікується:
function Person() {
this.age = 0;

setInterval(() => {
this.age++; // `this` відноситься до об'єкту person
}, 1000);
}

var p = new Person();

Дізнатися більше про 'Лексичному this' в стрілочних функціях на сайті MDN

3. Параметри за замовчуванням
ES6 дозволяє встановити параметри за замовчуванням при оголошенні функції. Ось простий приклад:
let getFinalPrice = (price, tax = 0.7) => price + price * tax;
getFinalPrice(500); // 850, так як значення tax не задано

getFinalPrice(500, 0.2); // 600, значення tax за замовчуванням замінюється на 0.2


4. Spread / Rest оператор
...
оператор назвают як spread або rest, в залежності від того, як і де він використовується.
При використанні в будь-якому итерируемом об'єкті (iterable), даний оператор "розбиває" ("spread") його на індивідуальні елементи:
function foo(x, y, z) {
console.log(x, y, z);
}

let arr = [1, 2, 3];
foo(...arr); // 1 2 3

Іншим поширеним використанням оператора
...
є об'єднання набору значень в один масив. У даному випадку оператор працює як "rest" від перекладача: не знайшов відповідного перекладу на російську мову, з прикладу нижче все стане ясно)
function foo(...args) {
console.log(args);
}
foo(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]


5. Розширення можливостей літералів об'єкта
ES6 дозволяє оголосити літерали об'єкта за допомогою короткого синтаксису для ініціалізації властивостей змінних і визначення функціональних методів. Також, стандарт забезпечує можливість обчислення властивостей безпосередньо в литерале об'єкта.
function getCar(make, model, value) {
return {
// з синтаксисом короткої записи можна
// пропускати значення властивості, якщо воно
// збігається з ім'ям змінної, значення
// якої ми хочемо використовувати
make, // аналогічно make: make
model, // аналогічно model: model
value, // аналогічно value: value

// обчислювані властивості тепер працюють в
// литералах об'єкта
['make' + make]: true,

// Коротка запис методу об'єкта пропускає
// ключове слово `function` і двокрапка. Замість
// "depreciate: function() {}" можна написати:
depreciate() {
this.value -= 2500;
}
};
}

let car = getCar('Kia', 'Sorento', 40000);
console.log(car);
// {
// make: 'Kia',
// model:'Sorento',
// value: 40000,
// makeKia: true,
// depreciate: function()
// }


6. Восьмеричний і двійковий літерали
У ES6 з'явилася нова підтримка для вісімкових і двійкових символів.
Додавання до початку числа
0o
або
0O
перетворює його у вісімкову систему числення (аналогічно,
0b
або
0B
перетворює в двійкову систему числення). Подивимося на наступний код:
let oValue = 0o10;
console.log(oValue); // 8

let bValue = 0b10;
console.log(bValue); // 2


7. Деструктуризация масивів та об'єктів
Деструктуризация допомагає уникнути використання допоміжних змінних при взаємодії з об'єктами і масивами.
function foo() {
return [1, 2, 3];
}
let arr = foo(); // [1,2,3]

let [a, b, c] = foo();
console.log(a, b, c); // 1 2 3

function bar() {
return {
x: 4,
y: 5,
z: 6
};
}
нехай { x: a, y: b, z: c } = bar();
console.log(a, b, c); // 4 5 6


8. Ключове слово super для об'єктів
ES6 дозволяє використовувати метод
super
(безклассовых) об'єктах з прототипами. Ось простий приклад:
var parent = {
foo() {
console.log("Привіт від Батька!");
}
}

var child = {
foo() {
super.foo();
console.log("Привіт від Дитини!");
}
}

Object.setPrototypeOf(child parent);
child.foo(); // Привіт від Батьків!
// Привіт від Дитини!


9. Рядкові шаблони і роздільники
ES6 предоставяляет більш простий спосіб вставки значення змінної або результату виразу (т. зв. "інтерполяцію"), які розраховуються автоматично.
  • \
    ${… }` використовується для обчислення значення змінної/вираження.
  • `` Зворотні лапки використовуються як роздільник для таких випадків.
let user = 'Список';
console.log(`Привіт, ${user}!`); // Привіт, Кевін!


10. for...of проти for...in
  • for of...
    використовується для перебору в циклі итерируемых об'єктів, наприклад, масивів.
let nicknames = ['di', 'boo', 'punkeye'];
nicknames.size = 3;
for (let nickname of nicknames) {
console.log(nickname);
}
// di
// boo
// punkeye

  • for...in
    використовується для перебору в циклі всіх доступних для перебору (перечіслімого) властивостей об'єкта.
let nicknames = ['di', 'boo', 'punkeye'];
nicknames.size = 3;
for (let nickname in nicknames) {
console.log(nickname);
}
// 0
// 1
// 2
// size


11. Map і WeakMap
ES6 представляє нові структури даних —
Map
та
WeakMap
. Насправді, ми використовуємо "Map" в JavaScript весь час. Кожен об'єкт можна представити як окремий випадок
Map
.
Класичний об'єкт складається з ключів (завжди в строковому вигляді) і значень, тоді як у
Map
для ключа і значення можна використовувати будь-яке значення (і об'єкти, і примітиви). Подивимося на цей код:
var myMap = new Map();

var keyString = "рядок",
keyObj = {},
keyFunc = function() {};

// встановлюємо значення
myMap.set(keyString, "значення, пов'язане зі 'рядок'");
myMap.set(keyObj, "значення, пов'язане з keyObj");
myMap.set(keyFunc, "значення, пов'язане з keyFunc");

myMap.size; // 3

// отримуємо значення
myMap.get(keyString); // "значення, пов'язане зі 'рядок'"
myMap.get(keyObj); // "значення, пов'язане з keyObj"
myMap.get(keyFunc); // "значення, пов'язане з keyFunc"

WeakMap
WeakMap
Map
, в якому ключі володіють нестійкими зв'язками, що дозволяє не заважати збирача сміття, видаляти елементи
WeakMap
. Це означає, що можна не турбуватися про втрату пам'яті.
варто відзначити, що в
WeakMap
на відміну від
Map
, кожен ключ має бути об'єктом.
Для
WeakMap
є тільки чотири методи:
delete(ключ)
,
has(ключ)
,
get(ключ)
та
set(ключ, значення)
.
let w = new WeakMap();
w.set('a', 'b');
// Uncaught TypeError: Invalid value used as weak key map

var o1 = {},
o2 = function(){},
o3 = window;

w.set(o1, 37);
w.set(o2, "azerty");
w.set(o3, undefined);

w.get(o3); // undefined, тому що це задане значення

w.has(o1); // true
w.delete(o1);
w.has(o1); // false


12. Set і WeakSet
Об'єкти Set це колекції унікальних значень. Неповторювані значення ігноруються, тому що колекція повинна містити тільки унікальні значення. Значення можуть бути примітивами або посиланнями на об'єкти.
let mySet = new Set([1, 1, 2, 2, 3, 3]);
mySet.size; // 3
mySet.has(1); // true
mySet.add('рядки');
mySet.add({ a: 1, b:2 });

Ви можете перебирати
Set
в циклі за допомогою
forEach
або
for of...
. Перебір відбувається в тому ж порядку, що і вставка.
mySet.forEach((item) => {
console.log(item);
// 1
// 2
// 3
// 'рядки'
// Object { a: 1, b: 2 }
});

for (let value of mySet) {
console.log(value);
// 1
// 2
// 3
// 'рядки'
// Object { a: 1, b: 2 }
}

Set
також є методи
delete()
та
clear()
.
WeakSet
Аналогічно
WeakMap
, об'єкт
WeakSet
дозволяє зберігати об'єкти з нестійкими зв'язками в колекції. Об'єкт
WeakSet
унікальний.
var ws = new WeakSet();
var obj = {};
var foo = {};

ws.add(window);
ws.add(obj);

ws.has(window); // true
ws.has(foo); // false, foo не був доданий до колекції

ws.delete(window); // видаляє з колекції window
ws.has(window); // false, window був видалений


13. Класи в ES6
У ES6 представили новий синтаксис для класів. Тут варто зазначити, що клас ES6 не являє собою нову об'єктно-орієнтовану модель успадкування. Це просто синтаксичний цукор для існуючого в JavaScript прототипного успадкування.
Клас у ES6 являє собою просто новий синтаксис для роботи з прототипами і функціями-конструкторами, які ми звикли використовувати в ES5.
Функції, записані за допомогою ключового слова
static
, використовуються для оголошення статичних властивостей класу.
class Task {
constructor() {
console.log("Створений екземпляр task!");
}

showId() {
console.log(23);
}

static loadAll() {
console.log("Завантажуємо всі tasks...");
}
}

console.log(typeof Task); // function
let task = new Task(); // "Створений екземпляр task!"
task.showId(); // 23
Task.loadAll(); // "Завантажуємо всі tasks..."

extends і super в класах
Подивимося на наступний код:
class Car {
constructor() {
console.log("Створюємо новий автомобіль");
}
}

class Porsche extends Car {
constructor() {
super();
console.log("Створюємо Porsche");
}
}

let c = new Porsche();
// Створюємо новий автомобіль
// Створюємо Porsche

У ES6 ключове слово
extends
дозволяє класу-нащадку успадковувати від батьківського класу. Важливо відзначити, що конструктор класу-нащадка повинен викликати super().
Також, в класі-нащадку можна викликати метод батьківського класу з допомогою
super.имяМетодаРодителя()
.
Дізнатися більше про класах на сайті MDN
Про що варто пам'ятати:
  • Оголошення класів не піднімаються нагору (not hoisted). Спочатку потрібно оголосити клас і тільки після цього використовувати його, інакше буде помилка ReferenceError.
  • Немає необхідності використовувати ключове слово
    function
    під час завдання визначення функцій всередині класу.


14. Тип даних Symbol
Symbol це унікальний і незмінний тип даних, представлений в ES6. Метою
Symbol
є створення унікального ідентифікатора, до якого можна отримати доступ.
Ось як можна створити
Symbol
:
var sym = Symbol("опціональне опис");
console.log(typeof sym); // symbol

Зауважимо, що використовувати
new
разом
Symbol(...)
не можна.
Якщо
Symbol
використовується як властивість/ключ об'єкта, він зберігається таким спеціальним чином, що властивість не буде показано при нормальному перерахування властивостей об'єкта.
var o = {
val: 10,
[Symbol("випадковий")]: "Я - символ",
};

console.log(Object.getOwnPropertyNames(o)); // val

Щоб отримати символічні властивості об'єкта, слід використовувати
Object.getOwnPropertySymbols(o)


15. Ітератори
Ітератор звертається до елементів колекції по одному, в той же час зберігаючи пам'ять про своєї поточної позиції в цій колекції. У ітератора є метод
next()
, який повертає наступний елемент послідовності. Цей метод повертає об'єкт з двома властивостями: done (закінчений чи перебір) і value (значення).
У ES6 є метод
Symbol.iterator
, який визначає ітератор для об'єкта за замовчуванням. При кожній необхідності перебору в циклі для об'єкта (наприклад, на початку циклу for..of), його метод ітератора викликається без аргументів, і повернутий ітератор використовується для того, щоб отримати значення для перебору.
Подивимося на масив, який є перебираемым (iterable), і на ітератор, який є у масиву для обробки його значень:
var arr = [11,12,13];
var itr = arr[Symbol.iterator]();

itr.next(); // { value: 11, done: false }
itr.next(); // { value: 12, done: false }
itr.next(); // { value: 13, done: false }

itr.next(); // { value: undefined, done: true }

Зауважимо, що можна написати власний ітератор через визначення
obj[Symbol.iterator]()
з описом об'єкта.
Детальніше про ітератори:
На сайті MDN

16. Генератори
Функції-генератори являють собою нову особливість ES6, яка функції дозволяє створювати багато значень протягом деякого періоду часу, повертаючи об'єкт (званий генератором), який може бути итерирован для викиду значень функції по одному за раз.
Функція-генератор повертає итерируемый об'єкт при своєму виклик.
Функція-генератор записується з допомогою знака
*
після ключового слова
function
, а в тілі функції повинно бути ключове слово
yield
.
function *infiniteNumbers() {
var n = 1;
while (true) {
yield n++;
}
}

var numbers = infiniteNumbers(); // повертає об'єкт перебираемый

numbers.next(); // { value: 1, done: false }
numbers.next(); // { value: 2, done: false }
numbers.next(); // { value: 3, done: false }

Кожного разу при виклику
yield
обчислене значення стає наступним значенням послідовності.
Також зауважимо, що генератори обчислюють свої повернені значення за запитом, що дозволяє їм ефективно представляти послідовності, витратні з точки зору обчислень, або навіть нескінченні послідовності.

17. Промисы
У ES6 з'явилася вбудована підтримка промисов. Промис це об'єкт, який чекає виконання асинхронної операції, після якого (тобто після виконання) промис приймає одне з двох станів: fulfilled (resolved, успішне виконання) або rejected (виконано з помилкою).
Стандартним способом створення промиса є конструктор
new Promise()
, який приймає обробник з двома функціями як параметрами. Перший обробник (зазвичай іменується
resolve
) являє собою функцію для виклику разом з майбутнім значенням, коли воно буде готове; другий обробник (зазвичай іменується
reject
), є функцією, яка викликається для відмови від виконання промиса, якщо він не може визначити майбутнє значення.
var p = new Promise(function(resolve, reject) { 
if (/* умова */) {
resolve(/* значення */); // fulfilled successfully (успішний результат)
} else {
reject(/* reason */); // rejected (помилка)
}
});

Кожен промис володіє методом
then
, в якому є два коллбэка. Перший коллбек викликається, якщо промис успішно виконано (resolved), тоді як другий коллбек викликається, якщо промис виконаний з помилкою (rejected).
p.then((val) => console.log("Промис успішно виконаний", val),
(err) => console.log("Промис виконаний з помилкою", err));

При поверненні значення
then
коллбэки передадуть значення наступного коллбэку
then
.
var hello = new Promise(function(resolve, reject) { 
resolve("Привіт");
});

hello.then((str) => `${str} Світ`)
.then((str) => `${str}!`)
.then((str) => console.log(str)) // Привіт Світ!

При поверненні промиса, успішно оброблене значення промиса пройде до наступного коллбэку, для того, щоб ефективно з'єднати їх разом.
Ця проста техніка допомагає уникнути пекла з коллбэками ("callback hell").
var p = new Promise(function(resolve, reject) { 
resolve(1);
});

var eventuallyAdd1 = (val) => {
return new Promise(function(resolve, reject){
resolve(val + 1);
});
}

p.then(eventuallyAdd1)
.then(eventuallyAdd1)
.then((val) => console.log(val)) // 3

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.