Pull to refresh

Стрелочные функции JS, быстро, просто и без проблем

Level of difficultyMedium

Всем привет!

Сегодня мы заберем важную тему → "Стрелочные функции 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))

Разберем содержание и начнем поэтапно =)

Содержание:

  1. Стрелочные функции: arguments, hoisting

  2. Работа с контекстом

  3. Методы присваивания контекста

  4. Обработчик событий


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

Здесь вы найдёте:

  • Часто задаваемые вопросы на собеседованиях

  • Полезные задачи

  • Обучающие статьи по написанию скриптов

Буду всегда рад помочь и поделиться новостями! Спасибо за чтение 🙂

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.