Стрелочные функции JS, быстро, просто и без проблем
Всем привет!
Сегодня мы заберем важную тему → "Стрелочные функции JS"
Почему это важно?
Потому что мы уже почти отказались от Function Declaration
.
Пример:
// Стрелочная функция
const x = (a, b) => a + b
console.log(x(2, 3)) // 5
// Function declaration
function y(a, b) {
return a + b
}
console.log(y(2, 3))
Разберем содержание и начнем поэтапно =)
Содержание:
Стрелочные функции:
arguments
,hoisting
Работа с контекстом
Методы присваивания контекста
Обработчик событий
1. Стрелочные функции: arguments, hoisting
Итак, стрелочные функции появились с ES6 (если это интересно). Очень удобная вещь, особенно когда это касается упрощения кода!
Пример:
const sayHi = () => console.log("Привет")
Выглядит очень удобно, не занимает много строк, а ещё радует момент — это hoisting
.
Hoisting
— это всплытие.
Могут всплывать переменные и функции.
Пример переменной:
console.log(a) // undefined
var a = 5
Переменная уже вызвана, не выдает ошибку, но пока undefined
.
Пример function declaration (обычной функции):
sayHello() // Привет
function sayHello() {
console.log("Привет")
}
Мы вызываем функцию, которая ещё не задана, но она вызывается и выполняется.
Это и называется hoisting
.
Пример стрелочной функции:
sayHi() // ❌ Ошибка: TypeError: sayHi is not a function
const sayHi = () => console.log("Привет")
А в стрелочных функциях выдает ошибку: "Нет такой функции x, дружище"
И это здорово — не будет проблем с работой функций и подменой!
У обычной функции есть arguments,
а у стрелочных функций такого нет. Что это такое?
Пример:
function test() {
console.log(arguments)
}
test('Первая', 'статья') // { 0: "Первая", 1: "статья" }
А вот стрелочные функции используют ...rest:
const test = (...args) => console.log(args)
test('Первая', 'статья') // ['Первая', 'статья']
Обязательно! Обратите внимание, что обычные функции выдают объект, а стрелочные функции выдают массив — потому что это ...rest
.
Если хотите также объектом, вот простой пример:
const test = (...args) => Object.assign({}, args)
console.log(test('Первая', 'статья')) // {0: "Первая", 1: "статья"}
Это добавляет объект с методом, можно еще покороче:
const test = (...args) => ({...args})
console.log(test('Первая', 'статья')) // {0: "Первая", 1: "статья"}
Что такое ( {...} ) ?
Если нам нужно выдать объект, то добавляем ()
:
const getUser = () => ({ name: "Ник Уайт"})
const people = getUser()
console.log(people.name)
без скобок вернёт undefined
, потому что интерпретирует {}
как тело функции
Если мы хотим использовать внутри функции несколько переменных, или несколько функций, методов и т.д. — то мы делаем это не в одну строку:
const sum = (a, b) => {
const result = a + b
return result
}
console.log(sum(2, 3)) // 5
Итак, мы разобрали работу и отличие обычных функций от стрелочных! Но это еще не все, очень важные темы еще впереди, предлагаю продолжить!)
2. Работа с контекстом
Контекст — очень важная тема, особенно при присваивании и использовании! В случае его потери могут возникнуть серьёзные проблемы, которые сложно отследить в коде, особенно если он состоит не из пары строк.
Разберем пример с обычной функцией:
const obj = {
name: 'Ник Уайт',
getUser: function () {
return this.name
}
}
const nick = obj
console.log(nick.getUser()) // Ник Уайт
Всё хорошо — контекст не теряется, и всё работает отлично, как и должно быть!
Мы создаём ключ name: 'Ник Уайт'
и обращаемся к нему через функцию getUser
, которая использует this.name
и возвращает нужный результат.
Пример стрелочной функции:
const obj = {
name: 'Ник Уайт',
getUser: () => {
return this.name
}
}
const nick = obj
console.log(nick.getUser()) // undefined
Казалось бы, да — код почти тот же, изменились лишь мелочи. Но теперь контекст меняется. А почему так происходит?
Происходит так, потому что стрелочные функции не имеют собственного this
— они берут его из внешнего контекста.
А внешний контекст здесь — это window
(в браузере), а не obj
.
(если бы была обычная функция, то this
указывал бы на obj
, и всё работало бы как надо)
const obj = {
name: 'Ник Уайт',
getUser: function() {
const name = this.name
const arrow = () => name
return arrow()
}
}
const nick = obj
console.log(nick.getUser())
Это один из способов использовать стрелочную функцию внутри обычной функции, когда нужно сохранить контекст this
или просто передать нужные данные в стрелку. Это удобно, когда, например, нужно вызывать функцию внутри setTimeout
, map
, filter
и т.д., где легко потерять контекст.
Обязательно! Это частые вопросы на собеседованиях, изучив и попрактиюсь пару раз, запомните это!
3. Методы присваивания контекста
Контекст можно присваивать и получать нужный результат, при этом функция будет общей и сможет работать с любым контекстом!
Пример:
const obj = {
name: 'Ник Уайт',
getUser: function() {
return this.name
}
}
const people = obj
console.log(obj.getUser.apply({name: 'Alex'})) // Alex
Простой пример: у нас есть объект с ключами name
и getUser
. В getUser
хранится функция, у которой контекст — сам объект с name
. Мы создаем переменную people
, которая ссылается на объект obj
, и через метод присваиваем контекст — объект с name: 'Alex'
. И это будет работать!
У нас есть 3 метода присваивания контекста: bind, call, apply.
bind
— возвращает новую функцию с привязанным this
function sayHello() {
return `Привет, ${this.name}`
}
const user = { name: 'Ник' }
const sayHelloToNick = sayHello.bind(user)
console.log(sayHelloToNick()) // Привет, Ник
bind
не вызывает функцию сразу, а просто создаёт копию с нужным контекстом.
call
— вызывает функцию сразу, передавая this
и аргументы
function greet(message) {
console.log(`${message}, ${this.name}`)
}
const user = { name: 'Ник' }
greet.call(user, 'Добро пожаловать') // Добро пожаловать, Ник
call
полезен, когда нужно быстро вызвать функцию с другим контекстом.
apply
— почти как call
, но аргументы передаём массивом
function greet(message, emoji) {
console.log(`${message}, ${this.name} ${emoji}`)
}
const user = { name: 'Ник' }
greet.apply(user, ['Салют', '👋']) // Салют, Ник 👋
apply
удобен, если аргументы уже есть в массиве.
Самый большой минус — методы присваивания не работают со стрелочными функциями! Почему? Потому что у стрелочных функций нет собственного this, они просто берут его из внешнего контекста при создании.
Что мы можем сделать?
создать функцию, внтури сделать пременную ссылающиеся на контекст и сделать callback или просто вызывать стрелочную функцию ссылающиеся на созданную переменную!
4. Обработчик событий
Вот мы и подошли к важной теме — обработка событий, особенно при использовании стрелочных и обычных функций!
В чём разница?
Обычная функция:
const button = document.querySelector('button')
button.addEventListener('click', function () {
console.log(this) // this указывает на саму кнопку
})
Здесь this
ссылается на элемент, который вызвал событие (в данном случае — button
). Это удобно, если нам нужно что-то изменить прямо на этом элементе.
Стрелочная функция:
const button = document.querySelector('button')
button.addEventListener('click', () => {
console.log(this) // this будет window (или undefined в strict)
})
В стрелочной функции this
не переопределяется и берётся из внешнего контекста. Поэтому внутри обработчика this
уже не будет ссылаться на кнопку.
Запомни: стрелочную функцию лучше не использовать в обработчиках событий, если тебе нужен
this
.
Итог:
Мы разобрали работу стрелочных функций, их отличие от обычных функций!
Они очень удобные в сокращении кода, а также имеют свои особенности и различия.
Стрелочные функции мы используем практически везде, особенно в React, где они помогают писать лаконичный и читаемый код!
Мой канал в Telegram: https://t.me/nickwhite_web
Там я выкладываю посты и новости по JavaScript и Web-разработке.
Хаб по JavaScript: https://github.com/n1ckwhite/JavaScript-Universe
Здесь вы найдёте:
Часто задаваемые вопросы на собеседованиях
Полезные задачи
Обучающие статьи по написанию скриптов
Буду всегда рад помочь и поделиться новостями! Спасибо за чтение 🙂