ECMAScript 2015 (или ES6) является шестым и основным изданием стандарта спецификации языка ECMAScript. Он определяет стандарт для реализации JavaScript.
ES6 внес значительные изменения в язык JavaScript. Он вводит несколько новых функций, таких как переменные в области блока, новый цикл для перебора массивов и объектов, литералы шаблонов и многие другие улучшения, делающие программирование на JavaScript проще и увлекательнее. В этой главе мы обсудим некоторые из лучших функций ES6, которые вы можете использовать в повседневном кодировании JavaScript.
Ключевое слово let
ES6 вводит новое ключевое слово let
для объявления переменных. До ES6 единственный способ объявить переменную в JavaScript был с использованием var
. Рассмотрим, в чем разница между ними.
Есть два критических различия между var
и let
. Переменные, объявленные с ключевым словом var
, находятся в верхней области, в то время как с переменными, объявленными ключевым словом let
можно работать только в блочной области, ограниченной фигурными скобками {}
.
Блочная область просто означает, что между парой фигурных скобок {}
создается новая область видимости. Поэтому, если вы объявляете переменную с ключевым словом let
внутри цикла, она не будет существовать вне цикла, как показано в следующем примере:
// ES6 syntax
for(let i = 0; i < 5; i++) {
console.log(i); // 0,1,2,3,4
}
console.log(i); // undefined
// ES5 syntax
for(var i = 0; i < 5; i++) {
console.log(i); // 0,1,2,3,4
}
console.log(i); // 5
Как видно из приведенного выше примера, переменная i
в первом блоке недоступна вне цикла for
. Это также позволяет нам многократно использовать одно и то же имя переменной, поскольку ее область ограничена блоком ({}
), что приводит к меньшему объему объявления и более чистому коду.
Ключевое слово const
Новое ключевое слово const
позволяет определять константы. Константы предназначены только для чтения; вы не можете переназначать им новые значения. Они также имеют блочную область видимости, как let
.
const PI = 3.14;
console.log(PI); // 3.14
PI = 10; // error
Однако вы все равно можете изменить свойства объекта или элементы массива:
// Изменяем значения свойства объекта
const PERSON = {name: "Peter", age: 28};
console.log(PERSON.age); // 28
PERSON.age = 30;
console.log(PERSON.age); // 30
// Изменяем элемент массива
const COLORS = ["red", "green", "blue"];
console.log(COLORS[0]); // красный
COLORS[0] = "yellow";
console.log(COLORS[0]); // желтый
Цикл for…of
Новый цикл for...of
позволяет нам очень легко перебирать массивы или другие итерируемые объекты. Кроме того, код внутри цикла выполняется для каждого элемента итерируемого объекта. Вот пример:
// Итерация по массиву
let letters = ["a", "b", "c", "d", "e", "f"];
for(let letter of letters) {
console.log(letter); // a,b,c,d,e,f
}
// Итерация по строкам
let greet = "Hello World!";
for(let character of greet) {
console.log(character); // H,e,l,l,o, ,W,o,r,l,d,!
}
Цикл for...of
не работает с объектами, потому что они не повторяются. Если вы хотите перебрать свойства объекта, вы можете использовать цикл for-in
.
Шаблонные литералы
Шаблонные литералы обеспечивают простой и понятный способ создания многострочных строк и выполнения интерполяции строк. Теперь мы можем встраивать переменные или выражения в строку в любом месте без каких-либо хлопот.
Шаблонные литералы создаются с использованием символа обратной галочки (` `
) вместо обычных двойных или одинарных кавычек. Переменные или выражения могут быть помещены в строку с использованием синтаксиса ${...}
. Сравните следующие примеры и посмотрите, насколько это полезно:
// Простая многострочная строка
let str = `The quick brown fox
jumps over the lazy dog.`;
// Строка со встроенными переменными и выражением
let a = 10;
let b = 20;
let result = `The sum of ${a} and ${b} is ${a+b}.`;
console.log(result); // Сумма 10 и 20 - 30.
В то время как в ES5, чтобы достичь того же, мы должны были написать что-то вроде этого:
// Многострочная строка
var str = 'The quick brown fox\n\t'
+ 'jumps over the lazy dog.';
// Создание строки с использованием переменных и выражения
var a = 10;
var b = 20;
var result = 'The sum of ' + a + ' and ' + b + ' is ' + (a+b) + '.';
console.log(result); // Сумма 10 и 20 - 30.
Значения по умолчанию для параметров функции
Теперь в ES6 вы можете указать значения по умолчанию для параметров функции. Это означает, что если при вызове функции не предоставлено никаких аргументов, будут использоваться эти значения параметров по умолчанию. Это одна из самых ожидаемых функций в JavaScript. Вот пример:
function sayHello(name='World') {
return `Hello ${name}!`;
}
console.log(sayHello()); // Hello World!
console.log(sayHello('John')); // Hello John!
В то время как в ES5, чтобы достичь того же, мы должны были написать что-то вроде этого:
function sayHello(name) {
var name = name || 'World';
return 'Hello ' + name + '!';
}
console.log(sayHello()); // Hello World!
console.log(sayHello('John')); // Hello John!
Функции Arrow
Функции со стрелками — еще одна интересная особенность ES6. Она обеспечивает более краткий синтаксис для написания функциональных выражений без ключевых слов function
и return
.
Функции со стрелками определяются с использованием нового синтаксиса, обозначения стрелки (=>
). Посмотрим, как это выглядит:
// Выражение функции
var sum = function(a, b) {
return a + b;
}
console.log(sum(2, 3)); // 5
// Та же функция со стрелкой
var sum = (a, b) => a + b;
console.log(sum(2, 3)); // 5
Как вы можете видеть, у функции нет ключевых слов function
и return
в объявлении.
Вы также можете опустить круглые скобки, т. е. ()
, если есть ровно один параметр, но вам всегда нужно будет использовать его, когда у вас более одного параметра.
Кроме того, если в теле функции имеется более одного выражения, вам необходимо заключить его в фигурные скобки ({}
). В этом случае вам также нужно использовать оператор return
для возврата значения.
Существует несколько вариантов написания функций стрелок. Вот наиболее часто используемые:
// Один параметр, один оператор
var greet = name => alert("Hi " + name + "!");
greet("Peter"); // Hi Peter!
// Несколько аргументов, одно утверждение
var multiply = (x, y) => x * y;
alert(multiply(2, 3)); // 6
// Один параметр, несколько операторов
var test = age => {
if(age > 18) {
alert("Adult");
} else {
alert("Teenager");
}
}
test(21); // Adult
// Несколько параметров, несколько операторов
var divide = (x, y) => {
if(y != 0) {
return x / y;
}
}
alert(divide(10, 2)); // 5
// Нет параметра, один оператор
var hello = () => alert('Hello World!');
hello(); // Hello World!
Существует важное различие между обычными функциями и функциями стрелок. В отличие от обычной функции, функция стрелки не имеет своего собственного this
, она берет его из внешней функции, в которой она определена. В JavaScript this
текущий контекст выполнения функции.
Чтобы понять это, давайте посмотрим на следующие примеры:
function Person(nickname, country) {
this.nickname = nickname;
this.country = country;
this.getInfo = function() {
// Контекст внешней функции (объект Person)
return function() {
// Внутренний контекст функции (Глобальный объект Window)
alert(this.constructor.name); // Window
alert("Hi, I'm " + this.nickname + " from " + this.country);
};
}
}
var p = new Person('Rick', 'Argentina');
var printInfo = p.getInfo();
printInfo(); // Hi, I'm undefined from undefined
Переписываем тот же пример, используя литералы шаблона ES6 и функцию стрелки:
function Person(nickname, country) {
this.nickname = nickname;
this.country = country;
this.getInfo = function() {
// Контекст внешней функции (объект Person)
return () => {
// Контекст внутренней функции (объект Person)
alert(this.constructor.name); // Person
alert(`Hi, I'm ${this.nickname} from ${this.country}`);
};
}
}
let p = new Person('Rick', 'Argentina');
let printInfo = p.getInfo();
printInfo(); // Hi, I'm Rick from Argentina
Как вы можете ясно видеть, ключевое слово this
в приведенном выше примере относится к контексту функции, содержащей функцию стрелки, которая является объектом Person (строка № 9) в отличие от предыдущего примера, где она ссылалась на глобальный объект Window (строка № 9).
Классы
В ECMAScript 5 и более ранних версиях классы никогда не существовали в JavaScript. Классы представлены в ES6, который выглядит аналогично классам в других объектно-ориентированных языках, таких как Java, PHP и т. д., однако работают они по другому. Классы ES6 облегчают создание объектов, реализацию наследования с помощью ключевого слова extends
и повторного использования кода.
В ES6 вы можете объявить класс, используя ключевое слово new class
, за которым следует имя класса. Имена классов пишутся в TitleCase
(т.е. с заглавной буквы первой буквы каждого слова).
class Rectangle {
// Class constructor
constructor(length, width) {
this.length = length;
this.width = width;
}
// Class method
getArea() {
return this.length * this.width;
}
}
// Класс Square наследуется от класса Rectangle
class Square extends Rectangle {
// Дочерний класс constructor
constructor(length) {
// Вызов родительского constructor
super(length, length);
}
// Дочерний класс method
getPerimeter() {
return 2 * (this.length + this.width);
}
}
let rectangle = new Rectangle(5, 10);
alert(rectangle.getArea()); // 50
let square = new Square(5);
alert(square.getArea()); // 25
alert(square.getPerimeter()); // 20
alert(square instanceof Square); // true
alert(square instanceof Rectangle); // true
alert(rectangle instanceof Square); // false
В приведенном выше примере класс Square наследуется от Rectangle с использованием ключевого слова extends
. Классы, которые наследуются от других классов, называются производными классами или дочерними классами.
Кроме того, вы должны вызвать super()
в конструкторе дочернего класса перед доступом к контексту (this
). Например, если вы опустите super()
и вызовете метод getArea()
для квадратного объекта, это приведет к ошибке, поскольку метод getArea()
требует доступа к ключевому слову this
.
В отличие от объявлений функций, объявления классов не поднимаются вверх. Объявления классов находятся во временной мертвой зоне (TDZ), пока выполнение не достигнет точки объявления классов, подобно объявлениям let
и const
. Следовательно, вам необходимо объявить свой класс перед тем, как получить к нему доступ, иначе возникнет ошибка ReferenceError
.
Modules
До ES6 не было встроенной поддержки модулей в JavaScript. Все приложения JavaScript, например, переменные в разных файлах JavaScript, имеют одинаковую область видимости.
ES6 представляет файловый модуль, в котором каждый модуль представлен отдельным файлом .js
. Теперь вы можете использовать оператор export
или import
в модуле для экспорта или импорта переменных, функций, классов или любых других объектов в / из других модулей или файлов.
Давайте создадим модуль, то есть файл JavaScript main.js
и поместим в него следующий код:
let greet = "Hello World!";
const PI = 3.14;
function multiplyNumbers(a, b) {
return a * b;
}
// Экспорт переменных и функций
export { greet, PI, multiplyNumbers };
Теперь создайте другой файл JavaScript app.js
со следующим кодом:
import { greet, PI, multiplyNumbers } from './main.js';
alert(greet); // Hello World!
alert(PI); // 3.14
alert(multiplyNumbers(6, 15)); // 90
Наконец, создайте HTML-файл test.html
и со следующим кодом и откройте этот HTML-файл в своем браузере, используя протокол HTTP (или используйте localhost
). Также обратите внимание на type="module"
в теге script
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ES6 Module Demo</title>
</head>
<body>
<script type="module" src="app.js"></script>
</body>
</html>
Оператор Rest
ES6 вводит параметр rest, который позволяет передавать произвольное количество параметров в функцию в виде массива. Это особенно полезно в ситуациях, когда вы хотите передать параметры в функцию, но не знаете, сколько их вам понадобится.
Параметр rest задается с помощью префикса именованного параметра оператором rest (...
), то есть тремя точками. Параметр rest может быть только последним в списке параметров и он может быть только один. Взгляните на следующий пример, чтобы увидеть, как он работает:
function sortNames(...names) {
return names.sort();
}
alert(sortNames("Sarah", "Harry", "Peter")); // Harry,Peter,Sarah
alert(sortNames("Tony", "Ben", "Rick", "Jos")); // John,Jos,Rick,Tony
Когда параметр rest является единственным параметром в функции, он получает все аргументы, переданные функции, в противном случае он получает остальные аргументы, которые превышают количество заданных параметров.
function myFunction(a, b, ...args) {
return args;
}
alert(myFunction(1, 2, 3, 4, 5)); // 3,4,5
alert(myFunction(-7, 5, 0, -2, 4.5, 1, 3)); // 0,-2,4.5,1,3
Не путайте термин оператор rest с REST (REpresentational State Transfer). Он не имеет ничего общего с веб-сервисами RESTful.
Оператор Spread
The spread operator, which is also denoted by (...
), performs the exact opposite function of the rest operator. The spread operator spreads out (i.e. splits up) an array и passes the values into the specified function, as shown in the following example:
Оператор spread, который также обозначается (...
), выполняет совершенно противоположную функцию оператора rest. Оператор spread распространяет (т.е. разделяет) массив и передает значения в указанную функцию, как показано в следующем примере:
function addNumbers(a, b, c) {
return a + b + c;
}
let numbers = [5, 12, 8];
// ES5 способ передачи массива в качестве аргумента функции
alert(addNumbers.apply(null, numbers)); // 25
// ES6 оператор spread
alert(addNumbers(...numbers)); // 25
Оператор spread также может использоваться для вставки элементов массива в другой массив без использования таких методов массива, как push()
, unshift()
concat()
и т. д.
let pets = ["Cat", "Dog", "Parrot"];
let bugs = ["Ant", "Bee"];
// Создание массива путем вставки элементов из других массивов
let animals = [...pets, "Tiger", "Wolf", "Zebra", ...bugs];
alert(animals); // Cat,Dog,Parrot,Tiger,Wolf,Zebra,Ant,Bee
Деструктурирование
Назначение деструктурирования — это выражение, упрощающее извлечение значений из массивов или свойств из объектов в отдельные переменные путем предоставления более короткого синтаксиса.
Существует два вида выражений присваивания деструктуризации — присвоение деструктурирования массива и объекта. Что ж, посмотрим, как работает каждый из них:
Деструктурирование массивов
До ES6, чтобы получить индивидуальное значение массива, нам нужно написать что-то вроде этого:
// ES5 syntax
var fruits = ["Apple", "Banana"];
var a = fruits[0];
var b = fruits[1];
alert(a); // Apple
alert(b); // Banana
В ES6 мы можем сделать то же самое всего в одну строку, используя назначение деструктурирования массива:
// ES6 syntax
let fruits = ["Apple", "Banana"];
let [a, b] = fruits; // Деструктурирование массива
alert(a); // Apple
alert(b); // Banana
Вы также можете использовать оператор rest в назначении деструктурирования массива, как показано здесь:
// ES6 syntax
let fruits = ["Apple", "Banana", "Mango"];
let [a, ...r] = fruits;
alert(a); // Apple
alert(r); // Banana,Mango
alert(Array.isArray(r)); // true
Деструктурирование объектов
В ES5 для извлечения значений свойств объекта нам нужно написать что-то вроде этого:
// ES5 syntax
var person = {name: "Peter", age: 28};
var name = person.name;
var age = person.age;
alert(name); // Peter
alert(age); // 28
Но в ES6 вы можете извлечь значения свойств объекта и легко присвоить их переменным следующим образом:
// ES6 syntax
let person = {name: "Peter", age: 28};
let {name, age} = person; // Деструктурирование объекта
alert(name); // Peter
alert(age); // 28
Большинство функций, которые мы обсуждали выше, поддерживаются в последних версиях основных веб-браузеров, таких как Google Chrome, Mozilla Firefox, Microsoft Edge, Safari и т. д.
В качестве альтернативы, вы можете использовать онлайновые транспортеры (компиляторы исходного кода), такие как Babel, бесплатно, чтобы перенести ваш текущий код ES6 в ES5 для лучшей совместимости с браузером, не упуская преимуществ улучшенного синтаксиса и возможностей ES6.