Шаблонные литералы
Шаблонные литералы заключены в обратные кавычки (` `) вместо двойных или одинарных. Они могут содержать подстановки, обозначаемые знаком доллара и фигурными скобками (${выражение}).
Пример:
var name = "bill";
console.log(`Hello ${name}`); // Hello bill
Директивы let const
Областью видимости переменных, объявленных ключевым словом let и const, является блок, в котором они объявлены, и все его подблоки. В этом работа директива let схожа с работой директивы var. Основная разница заключается в том, что областью видимости переменной, объявленной директивой var, является вся функция, в которой она объявлена. К тому же переменные объявленные с ключевым словом const являются неизменными константами. При декларации переменной с ключевым словом const необходимо использовать оператор присваивания для задания значения константе.
Пример отличия глобальной и блочной области видимости:
function myFunction() {
if (true)
let a = 5;
console.log(a); //SyntaxError так как a доступна только в блоке if
if (true)
var b = 5;
console.log(b); //5 так как b доступна во всей функции
}
Стрелочные функции
Выражения стрелочных функций имеют более короткий синтаксис по сравнению с функциональными выражениями и лексически привязаны к значению this (но не привязаны к собственному this, arguments, super, или new.target). Стрелочные функции всегда анонимные.
Примеры стрелочных функции:
const sum = (a, b) => a + b;
const squared = a => a*2;
const myFun = () => someFun();
const Fun = (data) => { data.response }
For...of
Оператор for...of выполняет цикл обхода итерируемых объектов (включая Array, Map, Set, объект аргументов и подобных), вызывая на каждом шаге итерации операторы для каждого значения из различных свойств объекта.
Почему НЕ СЛЕДУЕТ использовать for...in цикл для итерации? Потому что в зависимости от движка, JavaScript может итерировать в произвольном порядке, что может привести к непредсказуемому результату. Кроме того, итерационная переменная представляет собой строку, а не число, а значит, если вы собираетесь производить какие — либо подсчеты с переменной, вам потребуется провести конкатенацию строк вместо добавления. Так что во избежание логических ошибок не следует его использовать!
Синтаксис цикла for...of
for (var i of arr) //for (переменная of объект)
arr[i] = "something value"
Вычисляемые имена
Синтаксис объявления объектов и их элементов поддерживает вычисляемые имена свойств. Это позволяет добавлять в скобки [] выражение, которое будет вычислено, как имя свойства. Оно напоминает шаблонные литералы.
Пример вычисляемых имён:
var a = () => "world";
var a = {
["a" + (10 - 6)]: {
["some" + "string"]: true,
[10 + 20]: 10,
[`hello ${a()}`]: a()
}
}
Object.assign()
Метод Object.assign() используется для копирования значений всех собственных перечисляемых свойств из одного или более исходных объектов в целевой объект. После копирования он возвращает целевой объект.
Пример использования Object.assign():
var = newStudent = Object.assign({}, person, student);
Теперь newStudent содержит все свойства и методы person и student.
В ES5 при созданий дубликата объекта происходит эффект мутаций, когда изменения дубликата отражаются на родительском объекте.
var A = { a: 1 };
var B = A;
B.a = 2;
Казалось бы мы изменяем свойство объекта B НО после выполнения данного кода и B.a и A.a будут равны 2. Ситуацию можно исправить используя метод Object.assign().
var A = { a: 1 };
var B = Object.assign({}, A);
B.a = 2;
Теперь только B.a будет равно 2 а, A.a всё также равно 1.
В Object.assign() можно сразу задать значения свойства и декларировать новые передав их как аргумент.
var A = { a: 1 };
var B = Object.assign({ b: 3}, A, { a: 2 });
Создаётся свойство b и изменяем значение свойства а из родительского объекта.
И теперь B.a равно 2. B.b равно 3.
Оставшиеся параметры
Синтаксис оставшихся параметров функции позволяет представлять неограниченное множество аргументов в виде массива. Если последний именованный аргумент функции имеет префикс ..., то он автоматически становится массивом с элементами от 0 до theArgs.length в соответствии с актуальным количеством аргументов, переданных в функцию.
Пример использования синтаксиса оставшихся параметров:
function name (a, b, ...c) {}
name (0, 1, 2, 3,)
В этом примере a = 0 b = 1 c[0] = 2 c[1] = 3
Если оставшийся параметр не передан то он будет пустым массивом (в отличии от обычных параметров он никогда не будет undefined).
Данный синтаксис можно использовать не только в аргументах функции но и в других местах, например для копирования и объединения массивов:
var a = [ 0, 1, 2 ];
var b = [ 3, 4, 5 ];
var c = [ ...a, ...b ]; // [ 0, 1, 2, 3, 4, 5 ]
Параметры по умолчанию
Теперь можно определять значения по умолчанию (отличное от undefined) параметрам которые не были переданы в функцию.
Пример использования параметров по умолчанию:
function myFun(a=5) {
return a*a;
}
console.log(myFun()); // 25
Деструктурирующее присваивание
Синтаксис деструктурирующего присваивания в выражениях JavaScript позволяет извлекать данные из массивов или объектов при помощи синтаксиса, подобного объявлению массива или литералов в объекте.
Пример деструктурирующего присваивания:
var a, b, rest;
[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2
[a, b, ...rest] = [1, 2, 3, 4, 5];
console.log(a); // 1
console.log(b); // 2
console.log(rest); // [3, 4, 5]
({a, b} = {a:1, b:2});
console.log(a); // 1
console.log(b); // 2
Map и Set
Map — Объект содержащий пары ключ-значение и сохраняющий порядок вставки. Любое значение (как объекты, так и примитивы) могут быть использованы в качестве ключей.
Пример:
var myMap = new Map();
var keyObj = {},
keyFunc = function () {},
keyString = "a string";
// задание значений
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");
myMap.size; // 3
// получение значений
myMap.get(keyString); // "value associated with 'a string'"
myMap.get(keyObj); // "value associated with keyObj"
myMap.get(keyFunc); // "value associated with keyFunc"
myMap.get("a string"); // "value associated with 'a string'"
// потому что keyString === 'a string'
myMap.get({}); // undefined, потому что keyObj !== {}
myMap.get(function() {}) // undefined, потому что keyFunc !== function () {}
Отличие Map от Object:
- Ключами Объекта выступают Строки и Символы, в то время как любое значение может быть ключом Map, включая функции, объекты и примитивы.
- В отличие от Объектов, ключи в Map упорядочены. Таким образом, во время итерации Map, ключи возвращаются в порядке вставки.
- Вы легко можете получить количество элементов в Map с помощью свойства size, в то время как количество элементов Объекта может быть определено только вручную.
- Map — итерируемый объект и может быть итерирован напрямую, в то время как Объект требует ручного получения списка ключей и их итерации.
- Объект имеет прототип и поэтому имеет стандартный набор ключей, который, при неосторожности, может пересекаться с вашими ключами. С момента выхода ES5 это может быть изменено с помощью map = Object.create(null).
- Map может иметь более высокую производительность в случаях частого добавления или удаления ключей.
Свойства и методы:
- Map.prototype.size — Возвращает число пар ключзначение на Map
- Map.prototype.set(key, value) — Добавляет переданную пару ключзначение в Map. Если указанный ключ уже существует то он перезапишется новым значением.
- Map.prototype.get(key) — Возвращает значение переданного ключа. Если ключа нет то вернётся undefined
- Map.prototype.has(key) — Возвращает true если переданный ключ существует и false если его нет
- Map.prototype.delete(key) — Удаляет указанную пару ключзначение и возвращает true. Возвращает false если ключ не существует
- Map.prototype.clear() — Удаляет все пары ключзначение из Map
- Map.prototype.keys() — Возвращает итератор ключей на Map для каждого элемента
- Map.prototype.values() — Возвращает итератор значений на Map для каждого элемента
- Map.prototype.entries() — Возвращает итератор массива [key, value] на Map для каждого элемента
Set — Позволяет сохранять уникальные значения любого типа, как примитивы, так и другие типы объектов. Объекты Set представляют коллекции значений, по которым можно выполнить обход в порядке вставки элементов. Значение элемента в Set может присутствовать только в одном экземпляре, что обеспечивает его уникальность в коллекции Set. Во всём остальном не отличается от Map.
Классы
Конструкторы
class Student {
constructor(name) {
this.name = name;
}
}
var robert = new Student('Robert');
console.log(robert.name); // Outputs 'Robert'
При созданий новых объектов от класса будет запускаться constructor(), который необходим для инициализации объектов.
Методы
class Student {
constructor(name) {
this.name = name;
}
sayName() {
return "Hi my name is " + this.name;
}
static stMethod () {
// do something
}
}
var robert = new Student('Robert');
Student.stMethod(); // Статические методы можно вызвать только из класса а, не из объектов образованных от класса
console.log(robert.sayName()); // "Hi my name is Robert"
Геттеры и сеттеры
class Student {
constructor(name) {
this.name = name;
}
get Name() {
return this.name;
}
set Name(newName) {
if(typeof(newName) != "string")
throw new Error("Name is not a string!");
else
this.name = newName; // Robert
}
}
var robert = new Student('robert');
robert.Name = "Robert";
console.log(robert.Name);
- setter — Необходим для валидаций записаваемых параметров (как в примере выше)
- getter — Необходим для получения своиств (хотя их можно получить и напрямую). Не может иметь аргументов
В ES6 нет встроенной инкапсуляций, но ее можно организовать самому. Например вот так:
var Student = (function () {
let privateProps = new WeakMap();
class Person {
constructor(name, Age) {
this.name = name; // public
privateProps.set(this, {age: Age}); // private
}
get Age() {
return privateProps.get(this).age;
}
set Age (newAge) {
privateProps.set(this, {age: newAge});
}
}
return Person;
})();
var robert = new Student('Robert', 19);
robert.Age = 20;
console.log(robert.Age); // 20
Наследование
С помощью ключевого слова extends можно наследовать своиства и методы другого класса.
class Person {
constructor (age) {
this.age = age;
}
sayAge () {
return this.age;
}
}
class Student extends Person {
constructor (name, age) {
super(age);
this.name = name;
}
sayFull () {
return `Hello my name is ${this.name} and I'm ${super.sayAge()} years old`
}
}
var robert = new Student("Robert", 19);
console.log(robert.sayFull()); // Hello my name is Robert and I'm 19 years old
- В дочернем класса перед использованием this нужно вызвать super(), для вызова конструктора родительского класса.
- super() можно использовать для вызова методов родительского класса из дочернего.
Promise
Promise это новый способ делать асинхронные вычисления без состояния гонки.
Promise может находиться в трёх состояниях:
- ожидание (pending): начальное состояние, не выполнено и не отклонено.
- выполнено (fulfilled): операция завершена успешно.
- отклонено (rejected): операция завершена с ошибкой.
Создание обещаний (promise) выполняется следующим образом:
const myPromise = new Promise((resolve, reject) => {
resolve(someValue); // успешное завершение
reject("failure reason"); // неудача
});
Объект функции с двумя аргументами resolve и reject вызывает успешное выполнение обещания, второй отклоняет его.
Чтобы снабдить функцию функционалом обещаний, нужно просто вернуть в ней объект Promise.
С помощью .then прикрепляются обработчики выполнения и отклонения обещания.
В этом примере создаётся асинхронный http запрос:
const URL = "https://getfestivo.com/v1/holidays?api_key=f8f42551-eb66-49d2-bcba-b8e42727ddfb&country=US&year=2019"; // Этот API возвращает список праздников различных стран
function asyncHttpRequest (url) {
return new Promise((resolve, reject) => { // Возвращаем promise
if (url == undefined) // Если вдруг забыли передать url
reject(new Error("Expected url and received nothing"));
else // Если не забыли передать url :D
{
resolve(() => {
fetch(url).then((response) => { // Создаём запрос
return response.json(); // Извлекаем содержимое тела JSON из ответа
}).then((myJson) => {
return(console.log(myJson)); // Выводим результат запроса в консоль
});
});
}
}
);}
asyncHttpRequest(URL).then((result) => result(), (error) => console.log(error));
Итераторы
Iterable — Это объект, содержание которого можно перебрать.
Итерируемый объект отличается от не итерируемого тем что имеет специальный метод который возвращает объект, для доступа к которому используется специальный символ: Symbol.iterator
Iterable {
[Symbol.iterator]()
}
Обьект возврощяющий этод метод формально называется итератор.
У итератора есть всего лишь один метод next()
Iterator {
next();
}
Который возврощяет обьект (назавём его itreratorResult) c двумя своиствами done и value
IteratorResult {
done,
value
}
done указывает есть ли еще значение в перебираемой последовательности а value содержит следующий элемент последовательности.
Для перебора итерируемых объектов был добавлен цикл for...of. Зачем же тогда нужен итератор? Если честно… я не знаю. Итератор это служебный метод, и вам врятле придется иметь с ним дело. Но понимание того что он из себя представляет нужно.
Собственно так выглядит сам итератор:
function makeIterator(array){
var nextIndex = 0;
return {
next: function(){
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{done: true};
}
}
}
Генераторы
Генераторы — это специальный тип функции, который работает как фабрика итераторов. Функция становится генератором, если содержит один или более yield операторов и использует function* синтаксис.
А если говорить проще то генераторы это новый вид функций которые могут приостанавливать свое выполнение и возвращать промежуточный результат и возобновлять выполнение позже.
Давайте рассмотрим обычную функцию выполняющую некоторые математические вычисления и возвращающую результат:
function myFunction (a) {
a = a*2;
a = a - 1;
return a;
}
console.log(myFunction(5)); // 9
Теперь взгляните на аналогичную функцию генератор:
function* generator(a) {
a = a*2;
yield a;
a = a - 1;
yield a;
}
var it = generator(5);
console.log(it.next().value); // 10
console.log(it.next().value); // 9
Как говорилось раньше генераторы могут приостанавливать своё выполнение и возвращать промежуточный результат. Из этого примера видно что на момент первого вызова функция как бы приостанавливает свое выполнение на первом брекпоинте yield и возвращает результат первого выражения. Во втором вызове функция продолжает с предыдущего брекпоинта и двигается к следующему, возвращая результат уже следующего выражения.
Функции генераторы предоставляют мощный инструмент для написания сложных последовательных функций.
Symbol
Symbol — это уникальный и неизменяемый тип данных, который может быть использован как идентификатор для свойств объектов. Они являются служебными инструментами и поэтому вам вряд ли придётся с ними работать, но знать их нужно. Зачем же нужен новый тип данных? Он нужен для того что бы разработчики языка могли добавлять новые идентификаторы или свойства объектов при этом не резервируя строковые названия для этих свойств.
Так вы можете создать символьное свойство:
var user = {
name: "Alex",
[Symbol("password")]: "12hsK3I"
}
Для получения массива символьных объектов используйте свойство Object.getOwnPropertySymbols(obj);
Для получения доступа в любом месте вашего кода используйте методы Symbol.for() и Symbol.keyFor()
Автор: Elliot_001