
Дізнайтесь більше про нові кар'єрні можливості в EchoUA. Цікаві проекти, ринкова оплата, гарний колектив. Надсилайте резюме та приєднуйтеся до нас.
Шпаргалка для повсякденного використання, що містить підбірку порад з ES2015 [ES6] із прикладами. Діліться своїми порадами в коментарях!
var проти let / const
Крім
var,
нам тепер доступні 2 нові ідентифікатори для зберігання значень –let
іconst
. На відміну відvar
,let
іconst
мають блокову зону видимості.
Приклад використання var
:
var snack = 'Meow Mix';function getFood (food) { if (food) { var snack = 'Friskies';
return snack; } return snack;}getFood (false); // undefined
Ось що відбувається при заміні var
на let
:
let snack = 'Meow Mix';function getFood (food) { if (food) { let snack = 'Friskies'; return snack; } return snack;}getFood (false); // 'Meow Mix'
Така зміна в поведінці показує, що треба бути обережним при рефакторингу старого коду, в якому використовується var
. Проста заміна var
на let
може привести до непередбачуваної поведінки програми.
Примітка.
let
іconst
видимі лише у своєму блоці. Таким чином, спроба викликати їх до оголошення приведе доReferenceError
.
console.log (x); // ReferenceError: x is not definedlet x = 'hi';
Краща практика: залиште
var
у legacy-коді для подальшого ретельного рефакторингу. При роботі з новим кодом використайтеlet
для змінних, значення яких змінюватимуться, іconst
– для незмінних змінних.
Заміна функцій (IIFE), що негайно викликаються, на блоки (Blocks)
Зазвичай функції, що негайно викликаються, використовують для обгороджування значень в їх зонах видимості. У ES6 можна створювати блокові зони видимості.
(function (){ var food = 'Meow Mix';}());console.log (food); // Reference Error
Приклад ES6 Blocks:
{ let food = 'Meow Mix';};console.log (food); // Reference Error
Стрілочні функції
Часто при використанні вкладених функцій треба відокремити контекст this
від його лексичної зони видимості. Приклад наведений нижче:
function Person (name) { this.name = name;}Person.prototype.prefixName = function (arr) { return arr.map (function (character){
return this.name + character; // Cannot read property 'name' of undefined });};
Поширеним способом розв’язання цієї проблеми є зберігання контексту this
у змінній:
function Person (name) { this.name = name;}Person.prototype.prefixName = function (arr) { var that = this; // Store the context of this return arr.map (function (character) { return that.name + character; });};
Також ми можемо передати потрібний контекст this
:
function Person (name) { this.name = name;}Person.prototype.prefixName = function (arr) { return arr.map (function (character) { return this.name + character; }, this);};
Чи прив’язати контекст:
function Person (name) { this.name = name;}Person.prototype.prefixName = function (arr) { return arr.map (function (character) { return this.name + character; }.bind (this));};
Використовуючи стрілочні функції, лексичне значення this
не приховане, і наведений вище код можна переписати так:
function Person (name) { this.name = name;}Person.prototype.prefixName = function (arr) { return arr.map (character => this.name + character);};
Краща практика: використовуйте стрілочні функції завжди, коли Вам потрібно зберегти лексичне значення
this
.
Стрілочні функції зрозуміліші, коли використовуються для написання функцій, що просто повертають значення:
var squares = arr.map (function (x) { return x * x }); // Function Expression
const arr =[1, 2, 3, 4, 5];const squares = arr.map (x => x * x); // Arrow Function for terser implementation
Краща практика: використайте стрілочні функції замість функціональних виразів, коли це доречно.
Рядки
З приходом ES6 стандартна бібліотека значно збільшилася. Крім попередніх змін, з’явилися рядкові методи, такі як .includes ()
і .repeat ()
.
.includes ( )
var string = 'food';var substring = 'foo';console.log (string.indexOf (substring) > - 1);
Замість перевірки поверненого значення > - 1
для перевірки на наявність підрядка можна використати .includes ()
, що повертає логічну величину:
const string = 'food';const substring = 'foo';console.log (string.includes (substring)); // true
.repeat ( )
function repeat (string, count) { var strings =[]; while (strings.length < count) { strings.push (string); } return strings.join ('');}
У ES6 все набагато простіше:
// String.repeat (numberOfRepetitions) 'meow'.repeat (3); // 'meowmeowmeow'
Шаблонні літерали
Шаблонні літерали дають змогу використати в рядках спеціальні символи.
var text = "This string contains "double quotes" which are escaped".;
let text = 'This string contains "double quotes" which don't need to be escaped anymore.';
Шаблонні літерали також підтримують інтерполяцію, що робить задачу конкатенації рядків і значень:
var name = 'Tiger';var age = 13;console.log ('My cat is named ' + name + ' and is ' + age + ' years old.');
Значно простіше:
const name = 'Tiger';const age = 13;console.log ('My cat is named ${name} and is ${age} years old.');
У ES5 ми обробляли перенесення рядків у такий спосіб:
var text = ( 'catn' + 'dogn' + 'nickelodeon');
Чи так:
var text =[ 'cat', 'dog', 'nickelodeon'].join ('n');
Шаблонні літерали зберігають перенесення рядків:
let text = ( 'catdognickelodeon');
Шаблонні літерали також можуть обробляти вирази:
let today = new Date ();let text = 'The time and date is ${today.toLocaleString ()}';
Деструктуризація
Деструктуризація дозволяє нам витягати значення з масивів і об’єктів (навіть вкладених) і поміщати їх у змінні зручнішим способом.
Деструктуризація масивів
var arr =[1, 2, 3, 4];var a = arr[0];var b = arr[1];var c = arr[2];var d = arr[3];
let [a, b, c, d] = [1, 2, 3, 4];console.log (a); // 1console.log (b); // 2
Деструктуризація об’єктів
var luke ={ occupation: 'jedi', father: 'anakin'};var occupation = luke.occupation; // 'jedi'var father = luke.father; // 'anakin'
let luke ={ occupation: 'jedi', father: 'anakin'};let {occupation, father} = luke;console.log (occupation); // 'jedi'console.log (father); // 'anakin'
Модулі
До ES6 нам доводилося використати бібліотеки Browserify для створення модулів на клієнтській стороні, і require у Node.js. З ES6 ми можемо прямо використати модулі будь-яких типів (AMD і CommonJS).
Експорт в CommonJS
module.exports = 1;module.exports ={ foo: 'bar'};module.exports =['foo', 'bar'];
module.exports = function bar (){};
Експорт в ES6
У ES6 ми можемо використати різні види експорту.
Іменований експорт:
export let name = 'David';export let age = 25;
Експорт списку об’єктів:
function sumTwo (a, b) { return a + b;}function sumThree (a, b, c) { return a + b + c;}export { sumTwo, sumThree };
Також ми можемо експортувати функції, об’єкти і значення, просто використовуючи ключове слово export
:
export function sumTwo (a, b) { return a + b;}export function sumThree (a, b, c) { return a + b + c;}
І нарешті, можна експортувати зв’язування за умовчанням:
function sumTwo (a, b) { return a + b;}function sumThree (a, b, c) { return a + b + c;}let api ={ sumTwo, sumThree};export default api;/* Which is the same as * export { api as default }; */
Краща практика: завжди використайте метод
export default
наприкінці модуля. Це чітко покаже, що саме експортується, і заощадить час.
Імпорт в ES6
ES6 надає різні види імпорту. Ми можемо імпортувати цілий файл:
import 'underscore';
Важливо знати, що імпортування всього файлу приведе до виконання всього коду на зовнішньому рівні цього файлу.
Як і в Python, є іменований імпорт:
import { sumTwo, sumThree } from 'math/addition';
Який можна перейменовувати:
import { sumTwo as addTwoNumbers, sumThree as sumThreeNumbers
} from 'math/addition';
Крім того, можна імпортувати простір імен:
import * as util from 'math/addition';
І нарешті, список значень з модуля:
import * as additionUtil from 'math/addition';const { sumTwo, sumThree } = additionUtil;
Імпорт зі зв’язування за умовчанням набуває вигляду:
import api from 'math/addition';// Same as: import { default as api } from 'math/addition';
Експортування краще спрощувати, але іноді можна змішувати імпорт за умовчанням із чимось ще. Коли ми експортуємо так:
// foos.jsexport { foo as default, foo1, foo2 };
Імпортувати їх можна так:
import foo, { foo1, foo2 } from 'foos';
При імпортуванні модуля, експортованого з використанням синтаксису CommonJS (як в React), можна зробити:
import React from 'react';const { Component, PropTypes } = React;
Це можна спростити:
import React, { Component, PropTypes } from 'react';
Примітка. Експортовані значення – це зв’язування, а не посилання. Внаслідок цього зміна в одному модулі спричинить зміну в іншому.
Параметри
У ES5 було декілька способів обробки функцій з значеннями за умовчанням, невизначеними аргументами та іменованими параметрами. У ES6 усе це реалізується, причому зі зрозумілим синтаксисом.
Параметри за умовчанням
function addTwoNumbers (x, y) { x = x || 0; y = y || 0; return x + y;}
У ES6 можна просто вказати параметри функції за умовчанням:
function addTwoNumbers (x=0, y=0) { return x + y;}
addTwoNumbers (2, 4); // 6addtwonumbers (2); // 2addtwonumbers (); // 0
Залишкові параметри
У ES5 невизначена кількість аргументів оброблялася так:
function logArguments (){ for (var i=0; i < arguments.length; i++) { console.log (arguments[i]); }}
Використовуючи залишковий оператор, можна передавати невизначене число аргументів:
function logArguments (...args) { for (let arg of args) { console.log (arg); }}
Іменовані параметри
Одним із шаблонів ES5 для роботи з іменованими параметрами був шаблон options object, узятий з jQuery.
function initializeCanvas (options) { var height = options.height || 600; var width = options.width || 400; var lineStroke = options.lineStroke || 'black';}
Те саме можна отримати, використовуючи деструкцію як формальний параметр функції:
function initializeCanvas ( { height=600, width=400, lineStroke='black'}) { // Use variables height, width, lineStroke here }
Якщо ми хочемо зробити все значення опціональним, можна деструктурувати порожній об’єкт:
function initializeCanvas ( { height=600, width=400, lineStroke='black'} = {}) { // ... }
Оператор розширення
У ES5 можна було знайти максимум масиву, використовуючи метод apply
над Math.max
:
Math.max.apply (null, [- 1, 100, 9001, - 32]); // 9001
У ES6 можна використати оператор розширення для передачі масиву значень як параметрів функції:
Math.max (...[- 1, 100, 9001, - 32]); // 9001
Також можна інтуїтивно конкатенирувати масиви літералів:
let cities =['San Francisco', 'Los Angeles'];let places =['Miami',...cities, 'Chicago']; // ['Miami', 'San Francisco', 'Los Angeles', 'Chicago']
Класи
До ES6 класи треба було створювати, додаючи до функції-конструктора властивості, розширюючи прототип:
function Person (name, age, gender) { this.name = name; this.age = age; this.gender = gender;}Person.prototype.incrementAge = function (){ return this.age += 1;};
Розширені класи так:
function Personal (name, age, gender, occupation, hobby) { Person.call (this, name, age, gender); this.occupation = occupation; this.hobby = hobby;}Personal.prototype = Object.create (Person.prototype);Personal.prototype.constructor = Personal;Personal.prototype.incrementAge = function (){ Person.prototype.incrementAge.call (this); this.age += 20; console.log (this.age);};
ES6 надає дуже зручний синтаксичний цукор. Класи можна створювати в такий спосіб:
class Person { constructor (name, age, gender) { this.name = name; this.age = age; this.gender = gender; } incrementAge (){ this.age += 1; }}
Розширювати – використовуючи ключове слово extends
:
class Personal extends Person { constructor (name, age, gender, occupation, hobby) { super (name, age, gender); this.occupation = occupation; this.hobby = hobby; } incrementAge (){ super.incrementAge (); this.age += 20; console.log (this.age); }}
Краща практика: хоча такий синтаксис і приховує реалізацію, новачкам у ньому простіше розібратися, та й написаний код буде чистішим.
За матеріалами es6-cheatsheet
Київ, Харків, Одеса, Дніпро, Запоріжжя, Кривий Ріг, Вінниця, Херсон, Черкаси, Житомир, Хмельницький, Чернівці, Рівне, Івано-Франківськ, Кременчук, Тернопіль, Луцьк, Ужгород, Кам'янець-Подільський, Стрий - за статистикою саме з цих міст програмісти найбільше переїжджають працювати до Львова. А Ви розглядаєте relocate?