Шпаргалка по современному JavaScript
131К открытий135К показов
В данном материале собраны основные концепции и принципы современного JavaScript, которые помогут быстро найти и вспомнить необходимую фичу.
Содержание
- Объявление переменных var, const, let
- Стрелочные функции
- Параметры функции по умолчанию
- Деструктурирование объектов и массивов
- Методы массивов map/filter/reduce
- Оператор spread(…)
- Сокращение свойств объекта
- Объекты «промисы»
- Шаблонные литералы
- Теговые шаблоны
- Инструкции импорта и экспорта
- Ключевое слово this
- Классы
- Ключевые слова extends и super
- Функция async и оператор await
- Truthy/Falsy
- Анаморфизмы и катаморфизмы
- Генераторы
- Статические методы
Объявление переменных: var, const, let
В JavaScript переменные объявляют при помощи трёх операторов. При этом заданные через const
переменные изменить нельзя, а через var
и let
— можно. Поэтому если в коде предстоит изменить значение переменной, задавайте её с помощью let
, если нет — через const
.
Область переменных функции обозначает рамки их использования в коде.
- Оператор
var
. Объявленные с его помощью переменные работают в пределах функции и не доступны вне её.function myFunction() { var myVar = "Nick"; console.log(myVar); // "Nick" - myVar доступна в пределах функции}console.log(myVar); // выведет ошибку ReferenceError, myVar не доступна вне функцииДругой пример, демонстрирующий рамки области переменных:function myFunction() { var myVar = "Nick"; if (true) { var myVar = "John"; console.log(myVar); // "John" // myVar находится в пределах функции, мы изменили значение myVar с "Nick" на "John" } console.log(myVar); // команды блока if влияют на значение "John"}console.log(myVar); // выведет ошибку ReferenceError, myVar не доступна вне функцииПеременные при выполнении оператора присваивания перемещаются в начало, что называется «поднятие переменных». Часть кода:console.log(myVar) // не определена, поэтому ошибки нетvar myVar = 2;при выполнении понимается как:var myVar;console.log(myVar) // не определена, поэтому ошибки нетmyVar = 2; - Оператор
let
. Переменные, объявленные с его помощью, имеют блочную область видимости. Кроме того, они не доступны, пока не присвоены и пока их значение нельзя изменить в той же области. На примере, приведённом выше:function myFunction() { let myVar = "Nick"; if (true) { let myVar = "John"; console.log(myVar); // "John" // myVar имеет блочную область видимости, поэтому мы задали новое значение // оно не доступно вне области и не зависит от предыдущего значения myVar } console.log(myVar); // команды блока if НЕ влияют на "Nick"}console.log(myVar); // выведет ReferenceError, myVar не доступна вне функцииВот как это работает для не доступных до присвоения переменных, объявленных операторами let и const:console.log(myVar) // выведет ReferenceErrorlet myVar = 2;Переменные, объявленные с помощью var, доступны до присвоения. При использовании let и const — только после него. Этот феномен получил название «Временные мёртвые зоны». Кроме того, с помощью оператора let нельзя объявить другую переменную:let myVar = 2;let myVar = 3; // выведет SyntaxError - Оператор
const
. Переменные, объявленные с его помощью, также имеют блочную область видимости и не доступны до присвоения. Кроме того, их нельзя объявить или заново присвоить в той же области:const myVar = "Nick";myVar = "John" // выводит ошибку, присвоить заново нельзяconst myVar = "Nick";const myVar = "John" // выведет ошибку, объявить другое значение нельзяОднако тонкость заключается в том, что вы можете изменить значение объявленной оператором const переменной для объектов:const person = { name: 'Nick'};person.name = 'John' // будет работать, поскольку значение изменили, а не присвоили зановоconsole.log(person.name) // "John"person = "Sandra" // выведет ошибку, поскольку повторное присвоение не доступно для переменных, объявленных с помощью constи массивов:const person = [];person.push('John'); // будет работать, поскольку переменную person изменили, а не присвоили зановоconsole.log(person[0]) // "John"person = ["Nick"] // выведет ошибку, поскольку повторное присвоение не доступно для переменных, объявленных с помощью const
Стрелочные функции
Стрелочные функции ввели в обновлении ES6 как альтернативный способ объявления и использования функций.
- Краткость. Стрелочные функции короче традиционных, и каждый случай требует отдельного рассмотрения.Явная/неявная функция return. Явная функция предполагает использование return в коде:function double(x) { return x * 2; // функция возвращает x * 2 с использованием слова return в коде }Традиционный код отличается наличием ключевого слова: const double = (x) => { return x * 2; // явная функция }Поскольку перед return нет команд, стрелочные функции позволяют избежать дополнительной строки:const double = (x) => x * 2; // верно, возвращает x*2Мы убрали скобки и ключевое слово, но вывод всё равно x * 2. Если функция не возвращает значение (имеет побочные эффекты), это происходит ни явно, ни неявно. При необходимости возврата объекта, его заключают в скобки:const getPerson = () => ({ name: "Nick", age: 24 })console.log(getPerson()) // объект { name: "Nick", age: 24 } возвращается при помощи стрелочной функцииЕдинственный аргумент. Если функции присвоено одно значение, скобки можно исключить:const double = (x) => x * 2; // в этой функции один параметрconst double = x => x * 2; // в этой тоже один параметрОтсутствие аргументов. В случае если аргументы не заданы, необходимо поставить скобки, иначе синтаксис будет неверным: () => { // скобки присутствуют, всё работает const x = 2; return x; } => { // скобки отсутствуют, код не будет работать const x = 2; return x; }
- Привязка ключевого слова this к окружению. Чтобы понять тонкости, необходимо знать о поведении
this
в JavaScript. Для доступа к переменной в функции внутри функции, например, пришлось бы использоватьthat = this
илиself = this
. А в стрелочных функциях значениеthis
заимствуется из окружения, а не присваивается при создании нового ключевого слова. Например, используемsetTimeout
в функцииmyFunc
:function myFunc() { this.myVar = 0; var that = this; // that = this setTimeout( function() { // новое this создано из области that.myVar++; console.log(that.myVar) // 1 console.log(this.myVar) // не определено — см. объявление функции выше }, 0 );}Теперь в том же коде используем стрелочные функции:function myFunc() { this.myVar = 0; setTimeout( () => { // this из окружения, предполагает значение myFunc this.myVar++; console.log(this.myVar) // 1 }, 0 );}
Параметры функции по умолчанию
С выходом версии ES2015 значение по умолчанию для параметра функции задаётся при помощи следующего синтаксиса:
Параметр по умолчанию применяется в двух случаях: когда он не задан или задан параметр undefined
. При введении null
параметр по умолчанию не применится.
Деструктурирование объектов и массивов
Деструктурирование — создание новых переменных путём извлечения данных из объектов и массивов. Например, извлечение параметров this.props
из React-проектов.
- Объект. Рассмотрим для всех примеров объект:const person = { firstName: "Nick", lastName: "Anderson", age: 35, sex: "M"}Без деструктурирования:const first = person.firstName;const age = person.age;const city = person.city || "Paris";Применяя деструктурирование:const { firstName: first, age, city = "Paris" } = person; // так выглядит деструктурирование одной строкойconsole.log(age) // 35 — создана переменная age, которая равна person.ageconsole.log(first) // "Nick" — создана переменная first, значение которой соответствует person.firstNameconsole.log(firstName) // ReferenceError — person.firstName существует, но новая переменная называется firstconsole.log(city) // Paris — создана переменная city, а поскольку person.city не определена, city равна заданному по умолчанию значению "Paris".
- Параметры функции. Деструктурирование применяется для извлечения параметров объектов в функции.
Без деструктурирования:function joinFirstLastName(person) { const firstName = person.firstName; const lastName = person.lastName; return firstName + '-' + lastName;}joinFirstLastName(person); // Nick-AndersonИзвлекая параметр person, получим компактную функцию:function joinFirstLastName({ firstName, lastName }) { // Мы создаём переменные firstName и lastName, деструктурируя параметр person return firstName + '-' + lastName;}joinFirstLastName(person); // Nick-AndersonСо стрелочными функциями код становится существенно меньше:const joinFirstLastName = ({ firstName, lastName }) => firstName + '-' + lastName;joinFirstLastName(person); // Nick-Anderson - Массив. Рассмотрим массив:const myArray = ["a", "b", "c"];Без деструктурирования он выглядит так:const x = myArray[0];const y = myArray[1];С деструктурированием:const [x, y] = myArray; // вот оно!console.log(x) // "a"console.log(y) // "b"
Методы массивов map/filter/reduce
Методы массивов пришли в JavaScript из функционального программирования. Используя три этих метода, вы избегаете циклов for
и forEach
в большинстве ситуаций. Попробуйте вместо for
использовать совокупность map
, filter
и reduce
. В первый раз будет сложно, потому что придётся научиться мыслить иначе, но дальше привыкнете. Посчитаем для примера сумму оценок студентов с результатом 10 и выше, используя три этих метода:
Для более детального наглядного объяснения возьмём массив:
- Array.prototype.map().const doubledNumbers = numbers.map(function(n) { return n * 2;});console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12]Что мы тут сделали? .map выполняет итерацию каждого элемента массива numbers и перемещает их в функцию. Цель функции — обработать и вернуть новую переменную, чтобы map мог заменить её.Теперь посмотрим на функцию отдельно, чтобы было понятнее:const doubleN = function(n) { return n * 2; };const doubledNumbers = numbers.map(doubleN);console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12]Метод часто используется со стрелочными функциями:const doubledNumbers = numbers.map(n => n * 2);console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12]Используем numbers.map(doubleN) и получаем[doubleN(0), doubleN(1), doubleN(2), doubleN(3), doubleN(4), doubleN(5), doubleN(6)], что равно [0, 2, 4, 6, 8, 10, 12].В случае если возвращать массив нет необходимости и нужен цикл с побочными эффектами, используйте for/forEach.
- Array.prototype.filter().const evenNumbers = numbers.filter(function(n) { return n % 2 === 0; // верно, если значение n равно значению, и ложно, если n не равно значению});console.log(evenNumbers); // [0, 2, 4, 6]Метод также часто используется со стрелочными функциями:const evenNumbers = numbers.filter(n => n % 2 === 0);console.log(evenNumbers); // [0, 2, 4, 6].filter осуществляет итерацию каждого элемента массива numbers и добавляет их в функцию. Цель функции — вернуть булево значение и определить, нужно оно или нет. После чего фильтр возвращает массив только с добавленными значениями.
- Array.prototype.reduce(). Цель метода — сократить переменные до одной после итерации.const sum = numbers.reduce( function(acc, n) { return acc + n; }, 0 // значение накопительной переменной на первой ступени итерации);console.log(sum) // 21Метод также часто используется со стрелочными функциями:const sum = numbers.reduce((acc, n) => acc + n, 0);console.log(sum) // 21.reduce применяется к массиву и использует функцию как первый параметр. Однако в данном случае мы сталкиваемся с исключениями:функция .reduce учитывает 2 параметра: вызываемую на каждом шаге итерации функцию и значение накопительной переменной (acc) на первом шаге итерации.параметры функции: функция, принятая за значение .reduce, имеет 2 других — накопительную переменную (acc) и текущий элемент (n). Накопительная переменная равна возвращаемому на предыдущем шаге итерации значению функции. На первой ступени acc равна значению, принятому за второй параметр .reduce.Ступени итерации:acc = 0, поскольку мы ввели «0» вторым значением для reduce;n = 0, первый элемент массива number.Функция возвращает acc + n –> 0 + 0 –> 0acc = 0, значение, возвращённое функцией на предыдущей ступени итерации;n = 1, второй элемент массива number.Функция возвращает acc + n –> 0 + 1 –> 1acc = 1, значение, возвращённое функцией на предыдущей ступени итерации;n = 2, третий элемент массива number.Функция возвращает acc + n –> 1 + 2 –> 3acc = 3, значение, возвращённое функцией на предыдущей ступени итерации;n = 3, четвёртый элемент массива number.Функция возвращает acc + n –> 3 + 3 –> 6(…действия повторяются до последнего шага)acc = 15, поскольку это значение функция вернула на предыдущей ступениn = 6, последний элемент массива number.На последнем шаге функция возвращает acc + n –> 15 + 6 –> 21
Оператор spread(…)
Его добавили в обновлении ES2015, чтобы элементы итерации (например массива) можно было использовать в качестве нескольких элементов в коде.
- Объектные литералы.
Возьмём два массива:
В arr2
первый аргумент — массив, поскольку читает содержимое arr1
. Но нам нужно сделать arr2
массивом, состоящим из последовательности букв. Для этого используем spread и получим необходимый результат:
- Оставшиеся параметры функции. С их помощью мы можем создать массив с неучтёнными параметрами функции. Объект
arguments
относится к функциям, равным массиву переданных функции аргументов.
Давайте предположим, что нам нужно добавить студента с его оценками и посчитать средний балл. Не лучше ли сделать два параметра двумя разными значениями, а затем собрать массив из полученных данных для итерации? В этом нам и поможет оставшийся параметр.
- Расширение свойств объекта.
Сокращение свойств объекта
Зададим переменную для свойств объекта. Если её имя совпадёт с названием свойства, можно сделать следующее:
Если вы объявляли объектный литерал в версиях до ES2015 или хотели использовать переменную в качестве значения свойств объекта, то пришлось бы писать:
Слишком много повторений, не так ли? Поэтому с выходом ES2015 при совпадении переменной с названием свойства, достаточно такого кода:
Объекты «промисы»
Промис — это объект, который используется для упорядочивания синхронных и асинхронных операций. При использовании промисов код становится чище, поэтому они всё чаще встречаются в проектах.
AJAX-запрос при выполнении не является синхронным, поскольку ответ от ресурса идёт какое-то время. Он может вообще не прийти, если ресурс не доступен (404). Для решения этой проблемы в ES2015 добавили промисы, которые принимают 3 состояния:
- ожидание;
- выполнен;
- отклонён.
Представим, что нам нужно создать Ajax-запрос до ресурса X. Используем для этого метод jQuery.get()
:
Объект Promise выполняет функцию executor
с аргументами resolve
и reject
. Эти аргументы выполняются по завершении операции как и функции, которые переводят промис из состояния ожидания в состояние выполнения или отклонения. Функция executor
выполняется сразу после создания промиса в статусе ожидания. Как только аргументами функции становятся resolve
или reject
, промис использует необходимые методы.
Используем методы промиса, чтобы получить его выполнение или ошибку:
В случае сдержанного промиса выполняется resolve и функция с методом .then
. Иначе выполняется reject и функция с методом .catch
. Также обработчик будет выполнен при сдержанном или нарушенном промисе, что приведёт к отсутствию «состояния гонки» между завершением асинхронной операции и применением обработчика.
Шаблонные литералы
Допускают использование строковой интерполяции и многострочных литералов. Другими словами, это синтаксис, допускающий использование выражений JavaScript внутри строк.
Теговые шаблоны
Являются расширенной формой шаблонных литералов и позволяют разбирать их с помощью функции. При вызове функции первый аргумент содержит массив строковых значений между интерполированными значениями. Чтобы уместить их все, используйте оператор spread (...
). Библиотека styled-components написана с применением теговых шаблонов.
Ниже приведён небольшой пример их работы:
Другой интересный пример:
Инструкции импорта и экспорта
Экспорт функций/объектов из модулей ES6 и импорт значений из них.
- Именованные импорт/экспорт используют для нескольких величин (ими могут быть лишь объекты первого класса):
Несмотря на внешнюю схожесть именных импортов с деструктурированием, синтаксис отличается. Они не поддерживают значения по умолчанию или вложенное деструктурирование. Кроме того, можно использовать дополнительное имя, но синтаксис отличается от используемого при деструктурировании:
- Импорт/экспорт по умолчанию.
Для одного модуля доступен один экспорт по умолчанию. Экспорт может быть функцией, классом, объектом и т.д. Значение рассматривается как «основное», поскольку так его проще импортировать:
Экспорт функции:
Ключевое слово this
Поведение этого ключевого слова в JavaScript отличается от других языков и зависит от вызова функции.
Классы
JavaScript — прототип-ориентированный язык программирования. Классы ввели как синтаксический сахар для прототип-ориентированного наследования в ES6. Слово «класс» смутит вас, если вы знакомы с классами в других языках программирования. Попробуйте посмотреть иначе: прочитайте о прототипах и их поведении в JavaScript. До ES6 синтаксис прототипов выглядел следующим образом:
С синтаксисом классов в ES6:
Ключевые слова extends и super
Ключевое слово extends
используют для объявления классов или в выражениях класса для создания дочерних классов. Они получают свойства родительских классов, а также дают возможность добавить новые свойства и изменить заимствованные.
Ключевое слово super
вызывает функции родителя объекта, включая его конструктор. Его следует использовать:
- до ключевого слова
this
в конструкторе; - с вызовом
super(arguments)
при передаче аргументов конструктору класса; - как вызов дочернего класса
super.X()
для метода X родительского класса.
Если бы мы попытались использовать this
перед super()
в классе Square, появилась бы ошибка ReferenceError:
Функция async и оператор await
Для написания асинхронного кода в JavaScript появился синтаксис async
/await
. Цель нововведения — упростить использование промисов и расширить рамки действий с ними. Для лучшего понимания этого синтаксиса, рекомендуем сначала ознакомиться с промисами. await
может использоваться только внутри асинхронной функции.
async
/await
используется с промисами, но предполагают более императивный стиль кода. Оператор async
определяет асинхронную функцию и всегда возвращает промис. Оператор await
приостанавливает выполнение функции async
, пока промис не выполнен или отклонён:
Если в асинхронной функции достигается значение return
, промис приобретает возвращённое значение. При выводе ошибки промис переходит в статус «отклонён». В то же время при отсутствии возвращённого значения асинхронной функции, промис возвращается без значения по завершению выполнения асинхронной функции.
Оператор await
ожидает выполнения промиса.
Функция fetch
позволяет выполнить AJAX-запрос.
Выберем пользователя GitHub с помощью промисов и функции fetch
:
А теперь эквивалент с использованием async
/await
:
Async
/await
подходят в случае с цепочкой взаимосвязанных промисов. Например можно использовать их для получения токена, чтобы выделить пост блога в базе данных, а также информацию об авторе:
- Обработка ошибок.
Если не добавить блоки try
/catch
к выражению await
, неперехваченные исключения будут отклонять промис, возвращённый асинхронной функцией. При этом неважно, находятся ли они внутри асинхронной функции или возникли во время await
. Использование throw
внутри асинхронной функции равноценно отклонённому промису.
Вот так можно устранить ошибки при помощи промисов:
то же самое, но с async
/await
:
Truthy/Falsy
Значения, похожие на правду и на ложь в JavaScript, относятся к логическим выражениям. Пример логического выражения — проверка значения оператора if
. Значению присваивается true
, если оно не равно:
false
;0
;" "
(пустая строка);null
;undefined
;NaN
.
Примеры логических выражений:
- проверка значения оператора
if
.
myVar
может быть объектом первого класса (переменной, функцией, логическим значением), но будет считаться логическим выражением, поскольку проверяется в его контексте.
- после логического оператора NOT
!
:
Оператор возвращает false
, если одиночный операнд может стать истинным; иначе возвращает true
.
- C конструктором объектов логических значений:
- В трёхкомпонентном сравнении:
myVar
проверяется в контексте логического значения.
Будьте осторожны, сравнивая 2 значения. Значения объекта (которые должны стать истинными) не считаются логическими, но конвертируются в примитивный тип данных. При сравнении объекта с логическим значением, например [] == true
, он преобразуется в [].toString() == true
:
Анаморфизмы и катаморфизмы
- Анаморфизмы. Функции, с помощью которых объекты разворачиваются в более сложные структуры, содержащие объекты того же типа. Представим преобразование целого числа в ряд чисел:
- Катаморфизмы. Противоположность анаморфизмов: сворачивают объекты с более сложной структурой в простые. В примере
product
преображает несколько чисел в одно:
Генераторы
Ещё один способ записать функцию downToOne
— использовать генератор. Эти функции могут приостанавливать выполнение, вернуть промежуточный результат, а затем продолжить выполнение в любой момент. Чтобы инстанцировать объект-генератор, необходимо объявить функцию function *
. Перепишем функцию downToOne
с использованием генератора:
Генераторы возвращают итерируемые объекты. Функция next()
выполняется до ключевого слова yield
, которое возвращает значение во внешний код. Либо до функции yield *
, которая передаёт её другой функции-генератору. При возврате результата return
функция-генератор считается завершённой. Дальнейшие вызовы функции next()
не вернут новых значений.
Ключевое слово yield *
активирует следующую функцию-генератор во время итерации:
Статические методы
Ключевое слово static
используется в классах для определения статичных методов. Статичные методы функции, принадлежащие объекту класса, но не доступные другим объектам того же класса.
Вызов статического метода из другого статического метода осуществляется с помощью ключевого слова this
. Для нестатических методов этот подход не сработает:
Вызов статического метода из нестатического метода производится двумя способами:
- с использованием названия класса. Мы используем имя класса и вызываем статичный метод как свойство, например
ClassName.StaticMethodName
:
- С помощью конструктора. Статические методы можно вызвать как свойства объектов конструктора:
Читайте также:10 консольных команд для упрощения отладки JavaScript-кодаДетальный список инструментов для JavaScript
131К открытий135К показов