Що і як в ES6: хитрощі, кращі практики і приклади. Частина перша. let/const, блоки, стрілочні функції, рядки, деструктуризація, модулі, параметри, класи


Шпаргалка для повсякденного використання, що містить підбірку радих з 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

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *