Skip to main content

Примусове задання this при виклику

Примусово задати this можна методами call, apply і bind, а також через виклик функції як методу об'єкта, стрілкові функції, Reflect.apply і оператор new. Різниця між ними в тому, коли відбувається виклик і чи можна змінити контекст згодом.

Теорія

TL;DR

  • call(thisArg, a, b) викликає функцію одразу, аргументи через кому.
  • apply(thisArg, [a, b]) те саме, але аргументи масивом.
  • bind(thisArg) не викликає, а повертає нову функцію з прив'язаним this.
  • obj.method() задає this = obj неявно.
  • Стрілкові функції власного this не мають і беруть його із зовнішньої області.
  • Reflect.apply(fn, thisArg, args) це сучасний аналог apply.
  • new Func() створює новий об'єкт і перекриває навіть прив'язку через bind.

Швидкий приклад

javascript
function greet() { console.log(`Привіт, я ${this.name}`); } const user = { name: 'Maria' }; greet.call(user); // Привіт, я Maria

Тут this всередині greet примусово вказує на user.

call і apply: негайний виклик із заданим контекстом

Метод Function.prototype.call() викликає функцію одразу і дозволяє вказати this вручну:

javascript
function greet() { console.log(`Привіт, я ${this.name}`); } const user = { name: 'Maria' }; greet.call(user); // Привіт, я Maria

Метод apply() робить те саме, що й call, але приймає аргументи у вигляді масиву:

javascript
function introduce(city, job) { console.log(`Я ${this.name} з міста ${city}, працюю як ${job}`); } const user = { name: 'Maria' }; introduce.apply(user, ['Kyiv', 'Frontend-розробник']); // Я Maria з міста Kyiv, працюю як Frontend-розробник

bind: нова функція з прив'язаним контекстом

Метод bind() не викликає функцію одразу, а створює нову, у якої this уже «прив'язаний» назавжди:

javascript
function sayHi() { console.log(`Привіт, я ${this.name}`); } const user = { name: 'Maria' }; const sayHiBound = sayHi.bind(user); sayHiBound(); // Привіт, я Maria

Відмінність:

  • call / apply викликають одразу;
  • bind створює копію із закріпленим контекстом.

Неявний контекст: метод об'єкта і стрілкові функції

Якщо викликати функцію через крапку в об'єкта, this автоматично вказує на цей об'єкт:

javascript
const user = { name: 'Maria', sayHi() { console.log(`Привіт, я ${this.name}`); } }; user.sayHi(); // Привіт, я Maria

Це неявне встановлення this, але по суті те саме.

У стрілкових функцій немає власного this: вони «успадковують» його із зовнішньої області:

javascript
const user = { name: 'Maria', sayHiLater() { setTimeout(() => { console.log(`Привіт, я ${this.name}`); // this = user }, 1000); } }; user.sayHiLater(); // Привіт, я Maria

Якби всередині setTimeout була звичайна функція, this загубився б і став би window.

Reflect.apply, new і навмисна втрата контексту

Reflect.apply() це сучасний аналог apply з ES6: робить те саме, але безпечніше і декларативніше, бо не залежить від того, чи не перевизначили apply на самій функції:

javascript
function sayHello() { console.log(`Привіт, ${this.name}`); } const user = { name: 'Maria' }; Reflect.apply(sayHello, user, []); // Привіт, Maria

При виклику функції з new створюється новий об'єкт, і this всередині функції вказує на нього, навіть якщо інший this прив'язали через bind:

javascript
function User(name) { this.name = name; } const Bound = User.bind({ name: 'Maria' }); const u = new Bound('Alice'); console.log(u.name); // Alice, контекст із new перекриває bind

Якщо ж треба навпаки «відв'язати» this і викликати функцію без контексту, передають null або undefined:

javascript
function showThis() { console.log(this); } showThis.call(null); // у 'use strict' -> undefined

У нестрогому режимі null буде підмінено на глобальний об'єкт (window або global).

Підсумкова таблиця способів

СпосібВикликає одразуЯк задає thisАргументи
call(thisArg, a, b)ТакПримусовоЧерез кому
apply(thisArg, [a, b])ТакПримусовоМасивом
bind(thisArg, a, b)НіПрив'язує назавждиЧерез кому
obj.method()ТакНеявно (this = obj)Через кому
Reflect.apply()ТакПримусово (сучасний аналог)Масивом
new Func()ТакНовий об'єкт усередині функціїАргументи конструктора

Типові помилки

  • Прив'язують this до стрілкової функції. У стрілок немає власного this, тому call, apply і bind на нього не впливають, а перший аргумент просто ігнорується.
  • Шукають Reflect.call. Такого методу не існує: у Reflect є лише apply, тому аргументи завжди передають масивом.
  • Вважають, що bind викличе функцію. Він лише повертає нову функцію, і без дужок нічого не відбудеться.
  • Розраховують на window у строгому режимі. У модулях і класах this при null залишається undefined, тому this.name кине помилку.
  • Втрачають контекст при передачі методу. setTimeout(user.sayHi, 1000) передає лише функцію без об'єкта; потрібен user.sayHi.bind(user) або стрілкова обгортка.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.