12 концепций, которые прокачают ваш JavaScript
Эти 12 основополагающих концепций, конечно, не охватывают всего, что должен знать разработчик, но они важны для любого JS-программиста.
67К открытий67К показов
Несмотря на уровень знаний вы должны понимать основополагающие концепции языка. Эти 12 концепций, конечно, не охватывают всего, что должен знать разработчик, но важны для любого JS-программиста.
Присвоения примитивных и ссылочных типов
Понимание того, как JavaScript присваивает значение переменной, является залогом работающего кода. Без этого вы можете легко написать код, который непреднамеренно будет изменять значения переменных.
JavaScript всегда использует присвоение по значению. И это очень важно: когда присваиваемое значение является одним из 5 JavaScript’овых примитивов (Boolean, null, undefined, String и Number) — присваивается фактическое значение. Однако, когда присваиваемое значение является типом Array, Function или Object, присваивается ссылка на объект в памяти.
Например, в коде ниже переменной var2
присваивается значение var1
. Т. к. var1
является примитивом (String), то переменной var2
присваивается строковое значение var1
, и она может рассматриваться как отдельная (независимая) переменная. Соответственно, изменения var2
никак не отразятся на var1
.
А теперь попробуем то же самое с типом Object
.
Если бы вы ждали от кода выше такого же поведения, как и с примитивами, — это, вероятнее всего, вызвало бы ошибки. Подобное может быть особенно неудобным, если вы решите создать функцию, которая будет изменять какой-нибудь Object
.
Замыкания
Замыкания — важный паттерн для приватизации переменной. В примере ниже createGreeter
возвращает анонимную функцию, которой доступна переданная переменная greeting
со значением «Hello». После эта переменная будет доступна для sayHello
.
Или же более «правдоподобный» пример. У вас может быть некая функция apiConnect(apiKey)
, которая возвращает некоторые методы с использованием API ключа. В таком случае этот ключ нужно передать только один раз.
Деструктуризация
Деструктуризация — это просто способ извлечения свойств из объектов.
Если вам нужно извлечь свойство, дав ему другое имя, — делайте так:
В следующем примере деструктуризация применяется для «чистой» передачи объекта person
в функцию introduce
. Иначе говоря, деструктуризация может использоваться для непосредственного извлечения передаваемых параметров. Для тех, кто разрабатывает на React, это может показаться знакомым.
Spread
Нельзя не сказать об этой концепции. Spread
— это специальный оператор JavaScript, который позволяет расширять выражения в тех местах, где предусмотрено использование нескольких аргументов.
В следующем примере Math.max()
не может принять массив arr
, т. к. функции с таким аргументом не существует. Math.max()
принимает числа отдельными аргументами. Оператор spread
(три точки — ...
) используется для извлечения отдельных элементов из массива.
Rest-параметры
Rest-параметры позволяют передавать произвольное количество аргументов, принимая их в виде массива.
Методы массивов
С помощью методов массива в JavaScript можно добиться крутых (а порой и элегантных) способов трансформации данных. На StackOverflow часто можно наткнуться на вопросы о том, как работать с массивом объектов.
Ниже будут перечислены некоторые методы массива, сгруппированные по назначению. Однако это далеко не весь список.
map, filter, reduce
В методах map()
, filter()
и reduce()
иногда можно запутаться. Они полезны для трансформации массива или возвращения его агрегатного значения.
- map(): возвращает массив, в котором каждый элемент изменяется с помощью переданной функции.const arr = [1, 2, 3, 4, 5, 6];const mapped = arr.map(el => el + 20);console.log(mapped);// [21, 22, 23, 24, 25, 26]
- filter(): возвращает массив с теми элементами, в которых переданная функция возвращает
true
.const arr = [1, 2, 3, 4, 5, 6];const filtered = arr.filter(el => el === 2 || el === 4);console.log(filtered);// [2, 4] - reduce(): работа с элементами с сохранением промежуточного результата.const arr = [1, 2, 3, 4, 5, 6];const reduced = arr.reduce((total, current) => total + current);console.log(reduced);// 21
find, findIndex, indexOf
Эти методы очень похожи. Используйте их следующим образом:
- find(): возвращает первый элемент массива, удовлетворяющий определенному условию.const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];const found = arr.find(el => el > 5);console.log(found);// 6Опять же, обратите внимание, что все числа после 5 удовлетворяют условию, но возвращаться будет только первое значение. Это полезно в случаях, когда вы используете break при первом совпадении.
- findIndex(): метод идентичен
find()
, но вместо возвращаемого значения здесь возвращается индекс первого подходящего элемента.const arr = ['Nick', 'Frank', 'Joe', 'Frank'];const foundIndex = arr.findIndex(el => el === 'Frank');console.log(foundIndex);// 1 - indexOf(): метод идентичен
findIndex()
, но вместо функции он принимает искомое значение. Используйте этот метод в тех случаях, когда вам не нужна функция для проверки элемента на совпадение.const arr = ['Nick', 'Frank', 'Joe', 'Frank'];const foundIndex = arr.indexOf('Frank');console.log(foundIndex);// 1
push, pop, shift, unshift
Вот отличные методы, которые позволяют добавлять или убирать элементы в массиве.
- push(): этот метод относительно простой. Он добавляет элемент в конец массива. Метод модифицирует массив и одновременно возвращает добавленный элемент.let arr = [1, 2, 3, 4];const pushed = arr.push(5);console.log(arr);// [1, 2, 3, 4, 5]console.log(pushed);// 5
- pop(): метод удаляет последний элемент массива. Как и в прошлом случае, метод изменяет массив и одновременно возвращает удалённый элемент.let arr = [1, 2, 3, 4];const popped = arr.pop();console.log(arr);// [1, 2, 3]console.log(popped);// 4
- shift(): этот метод удаляет первый элемент массива. Метод изменяет массив и возвращает удалённый элемент.let arr = [1, 2, 3, 4];const shifted = arr.shift();console.log(arr);// [2, 3, 4]console.log(shifted);// 1
- unshift(): добавляет один или несколько элементов в начало массива. Как и прошлые методы, он изменяет массив, но возвращает новую длину массива.let arr = [1, 2, 3, 4];const unshifted = arr.unshift(5, 6, 7);console.log(arr);// [5, 6, 7, 1, 2, 3, 4]console.log(unshifted);// 7
splice, slice
Эти методы либо изменяют, либо возвращают подмассив элементов.
- splice(): метод изменяет массив, удаляя или заменяя существующий элемент, и/или добавляет новый. Метод только изменяет массив. Код ниже можно объяснить так: в позиции 1 массива удалить 0 элементов и вставить b.let arr = ['a', 'c', 'd', 'e'];arr.splice(1, 0, 'b')
- slice(): возвращает подмассив элементов массива, начиная и заканчивая на определённой позиции. Если конечная позиция не указана, возвращается остаток массива. Важно понимать, что этот метод не модифицирует массив, а только возвращает подмассив.let arr = ['a', 'b', 'c', 'd', 'e'];const sliced = arr.slice(2, 4);console.log(sliced);// ['c', 'd']console.log(arr);// ['a', 'b', 'c', 'd', 'e']
sort
Метод сортирует массив, основываясь на функции, которая принимает первый и второй элемент. Этот метод изменяет сам массив. Если в массиве порядок элементов не был изменен, метод возвращает 0, если изменён — 1.
Генераторы
Не забываем и про них. Генератор определяет, какое значение будет возвращено при следующем вызове next()
.
А вот пример использования генератора для бесконечных значений:
Разница операторов сравнения (===) и (==)
Не стоит пренебрегать разницей этих операторов. Оператор (==
) перед операцией сравнения будет выполнять преобразование типов, а (===
) делать преобразования не будет.
Сравнение объектов
Частая ошибка у новичков в JavaScript — это неправильное сравнение объектов. Дело в том, что переменные объектов хранят в памяти ссылку на объект, а не сам объект. Один из способов сравнения двух объектов — предварительное преобразование их в JSON строку. Однако у этого способа есть недостаток: не факт, что порядок в объекте сохранится. Более безопасный способ сравнения объектов — использование специальной библиотеки, которая сравнивает объекты на более глубоком уровне (к примеру isEqual от loadash)
Хоть следующие объекты кажутся одинаковыми, они ссылаются на разные объекты в памяти.
А вот в следующем примере сравнение возвращает true
, потому что второй объект приравнивается к первому и они оба ссылаются на один объект.
Важно понимать разницу между примитивами и ссылочными типами для правильной установки переменной, которая будет ссылаться на объект в памяти.
Callback-функции
Многие избегают callback-функции (функции обратного вызова). А зря — ведь это довольно просто! В следующем примере console.log()
будет передан в myFunc()
в качестве callback-функции.
Промисы
Как только вы начнёте понимать работу callback’ов в JavaScript, возможно, совсем скоро вы окажетесь в «аду обратных вызовов». На помощь приходят промисы (англ. Promises). Оберните свою асинхронную логику в промисы: resolve
— для успехов, reject
— для фейлов. Используйте then
для обработки успеха и catch
— для обработки фейлов.
Async Await
Как только вы разберётесь в промисах, вам может понравиться async
await
— это«синтаксический сахар» поверх промисов. Ниже пример async
функции с await
и промисом.
Заключение
Если вы не знали ни об одной из этих 12 концепций — не стоит откладывать их изучение в долгий ящик. А если знали — практика лишней никогда не будет.
67К открытий67К показов