27 січня 2024 р.

Повторення стрілкових функцій

Повернемося до стрілкових функцій.

Стрілкові функції – це не просто «скорочення» для написання усіляких дрібниць. Вони мають деякі дуже специфічні та корисні особливості.

При написанні JavaScript-коду часто виникають ситуації, коли потрібно написати невелику функцію, яка буде виконана десь ще.

Наприклад:

  • arr.forEach(func)func виконується forEach для кожного елемента масиву.
  • setTimeout(func)func виконується вбудованим планувальником.
  • …тощо.

Створення функції та передача її кудись – це в дусі JavaScript.

І в таких функціях ми зазвичай не хочемо виходити з поточного контексту. В таких випадках і корисні стрілкові функції.

Стрілкові функції не мають “this”

Як ми пам’ятаємо з розділу Методи об’єкта, "this", стрілкові функції не мають this. Якщо відбувається звернення до this, його значення береться ззовні.

Наприклад, ми можемо використовувати це для ітерації всередині методу об’єкта:

let group = {
  title: "Наша група",
  students: ["Іван", "Петро", "Марія"],

  showList() {
    this.students.forEach(
      student => alert(this.title + ': ' + student)
    );
  }
};

group.showList();

Тут у forEach використовується стрілкова функція, тому this.title у ній має таке ж саме значення, як і у методі showList, – group.title.

Якби ми використали “звичайну” функцію, була б помилка:

let group = {
  title: "Наша група",
  students: ["Іван", "Петро", "Марія"],

  showList() {
    this.students.forEach(function(student) {
      // Error: Cannot read property 'title' of undefined
      alert(this.title + ': ' + student);
    });
  }
};

group.showList();

Помилка виникає через те, що forEach запускає функції з this=undefined за замовчуванням, тому ми намагаємося звернутися до undefined.title.

Це не впливає на стрілкові функції, тому що вони не мають власного this.

Стрілкові функції не можна використовувати з new

Відсутність this призводить до ще одного обмеження: стрілкові функції не можуть бути використані як конструктори. Їх не можна викликати з new.

Стрілкові функції проти bind

Між стрілковою функцією => та звичайною функцією, що викликається з .bind(this) є невеличка різниця:

  • .bind(this) створює “зв’язану версію” функції.
  • Стрілка => не створює жодних прив’язок. Ця функція просто не має this. Пошук this здійснюється так само, як і звичайний пошук змінних: у зовнішньому лексичному середовищі.

Стрілкові функції не мають “arguments”

Стрілкові функції також не мають змінної arguments.

Це чудово підходить для створення декораторів, коли нам потрібно прокинути виклик з поточними this та arguments.

Наприклад, defer(f, ms) приймає функцію і повертає обгортку над нею, яка відкладає виклик на ms мілісекунд:

function defer(f, ms) {
  return function() {
    setTimeout(() => f.apply(this, arguments), ms);
  };
}

function sayHi(who) {
  alert('Привіт, ' + who);
}

let sayHiDeferred = defer(sayHi, 2000);
sayHiDeferred("Іван"); // "Привіт, Іван" через 2 секунди

Те ж саме без стрілкової функції виглядатиме так:

function defer(f, ms) {
  return function(...args) {
    let ctx = this;
    setTimeout(function() {
      return f.apply(ctx, args);
    }, ms);
  };
}

Тут нам довелося створити додаткові змінні args and ctx, щоб функція всередині setTimeout могла їх прийняти.

Підсумки

Стрілкові функції:

  • Не мають this
  • Не мають arguments
  • Не можуть бути викликані з new
  • Також вони не мають super, але до цієї теми ми ще не дійшли. Ми про це поговоримо у розділі Наслідування класу

Це все тому, що вони призначені для невеличких фрагментів коду, які не мають власного “контексту”, а скоріше працюють у поточному. І вони дійсно дуже добре впорюються з цим завданням.

Навчальна карта