Пошук уроків, статей та іншого контенту
Закріпите контекст функції за допомогою bind і навчитеся створювати частково застосовані функції.
bindМетод bind створює нову функцію, у якій заздалегідь зафіксовано значення this. Оригінальна функція при цьому не змінюється.
Синтаксис:
const boundFunction = functionName.bind(thisArg, arg1, arg2);thisArg — значення, яке буде використано як this;
arg1,
результатом є нова функція.
Розглянемо приклад:
"use strict";
const user = {
name: "Олена",
};
function introduce(greeting) {
return `${greeting}, мене звати ${this.name}`;
}
const introduceUser = introduce.bind(user);
console.log(introduceUser("Привіт"));
// Привіт, мене звати ОленаВиклик introduce.bind(user) не запускає introduce. Він створює нову функцію introduceUser, для якої this назавжди прив’язано до об’єкта user.
У JavaScript значення this залежить не від місця оголошення функції, а від способу її виклику.
"use strict";
const user = {
name: "Олена",
sayName() {
return this.name;
},
};
console.log(user.sayName());
// Олена
const sayName = user.sayName;
console.log(sayName());
// TypeError або undefined залежно від режиму виконанняУ першому випадку метод викликається через user.sayName(), тому this посилається на user.
У другому випадку метод зберігається в окрему змінну та викликається як звичайна функція. Зв’язок з об’єктом втрачається.
bind"use strict";
const user = {
name: "Олена",
sayName() {
return this.name;
},
};
const sayName = user.sayName.bind(user);
console.log(sayName());
// ОленаТепер sayName має зафіксований контекст і працює незалежно від способу виклику.
bindbind може фіксувати не лише this, а й аргументи функції.
function multiply(firstNumber, secondNumber) {
return firstNumber * secondNumber;
}
const double = multiply.bind(null, 2);
console.log(double(10));
// 20
console.log(double(7));
// 14У цьому прикладі:
null використано як thisArg, оскільки функція не звертається до this;
перше число 2 зафіксовано;
аргумент, переданий під час виклику double, стає secondNumber.
Такий підхід називають частковим застосуванням: з функції з кількома аргументами створюється нова функція з меншою кількістю аргументів.
Часткове застосування корисне, коли певні параметри відомі заздалегідь і використовуються багато разів.
function formatPrice(currency, locale, amount) {
return new Intl.NumberFormat(locale, {
style: "currency",
currency,
}).format(amount);
}
const formatInUah = formatPrice.bind(null, "UAH", "uk-UA");
console.log(formatInUah(1250));
// 1 250,00 ₴
console.log(formatInUah(99.5));
// 99,50 ₴Функція formatPrice очікує три аргументи:
formatPrice(currency, locale, amount);Після застосування bind значення "UAH" і "uk-UA" уже зафіксовані:
formatInUah(amount);Це робить код коротшим і зменшує дублювання.
Під час передавання методу як обробника події його контекст може бути не тим, якого очікує код.
class Counter {
constructor() {
this.value = 0;
// Фіксуємо контекст методу для подальшого виклику
this.increment = this.increment.bind(this);
}
increment() {
this.value += 1;
console.log(`Поточне значення: ${this.value}`);
}
}
const counter = new Counter();
const button = document.querySelector("#increase");
button.addEventListener("click", counter.increment);HTML для цього прикладу:
<button id="increase">Збільшити</button>Якби метод не був прив’язаний у конструкторі, під час виклику через addEventListener він міг би втратити посилання на екземпляр Counter.
Замість bind у класах часто використовують стрілочні властивості, але bind залишається універсальним і працює з будь-якими звичайними функціями.
bind не змінює оригінальну функціюМетод bind завжди повертає нову функцію.
"use strict";
const context = {
value: 10,
};
function getValue() {
return this.value;
}
const boundGetValue = getValue.bind(context);
console.log(getValue === boundGetValue);
// falseОригінальна функція getValue залишається без змін. Це означає, що можна створювати кілька версій однієї функції з різними контекстами.
const firstContext = { value: 10 };
const secondContext = { value: 20 };
function getValue() {
return this.value;
}
const getFirstValue = getValue.bind(firstContext);
const getSecondValue = getValue.bind(secondContext);
console.log(getFirstValue());
// 10
console.log(getSecondValue());
// 20bindАргументи можна додавати частинами.
function sum(first, second, third) {
return first + second + third;
}
const addTen = sum.bind(null, 10);
const addTenAndTwenty = addTen.bind(null, 20);
console.log(addTenAndTwenty(5));
// 35Після першого виклику:
addTen(20, 5);Перший аргумент уже дорівнює 10.
Після другого bind функція фактично очікує лише третій аргумент:
addTenAndTwenty(5);Під час часткового застосування аргументи додаються зліва направо.
bind, call і applyУсі три методи дозволяють явно вказати this, але поводяться по-різному:
call одразу викликає функцію та приймає аргументи окремо;
apply одразу викликає функцію та приймає аргументи у вигляді масиву або масивоподібного об’єкта;
bind не викликає функцію одразу, а повертає нову прив’язану функцію.
function describe(prefix, suffix) {
return `${prefix}${this.name}${suffix}`;
}
const user = {
name: "Андрій",
};
console.log(describe.call(user, "Користувач: ", "."));
// Користувач: Андрій.
console.log(describe.apply(user, ["Користувач: ", "."]));
// Користувач: Андрій.
const describeUser = describe.bind(user, "Користувач: ");
console.log(describeUser("."));
// Користувач: Андрій.Вибирайте метод залежно від завдання:
потрібно викликати функцію зараз — call або apply;
потрібно підготувати функцію для майбутнього виклику — bind.
bind і стрілочні функціїСтрілочні функції не мають власного this. Вони використовують this із зовнішньої області видимості, тому bind не може змінити їхній контекст.
const user = {
name: "Олена",
};
const arrowFunction = () => this.name;
const boundArrowFunction = arrowFunction.bind(user);
console.log(boundArrowFunction());
// Значення залежить від зовнішнього контексту, але не від userДля функцій зі звичайним синтаксисом function bind може встановити this. Для стрілочних функцій це не працює.
const user = {
name: "Олена",
};
function regularFunction() {
return this.name;
}
const boundRegularFunction = regularFunction.bind(user);
console.log(boundRegularFunction());
// ОленаПрив’язану функцію можна використовувати з оператором new. У такому разі прив’язане значення this ігнорується: new створює власний новий об’єкт.
function User(name, role) {
this.name = name;
this.role = role;
}
const Admin = User.bind(null, "Адміністратор");
const admin = new Admin("admin");
console.log(admin.name);
// Адміністратор
console.log(admin.role);
// admin
console.log(admin instanceof User);
// trueПерший аргумент конструктора був зафіксований через bind, а другий передано під час виклику з new.
lengthУ прив’язаної функції властивість length показує приблизну кількість аргументів, які ще очікуються.
function createUser(name, role, isActive) {
return { name, role, isActive };
}
const createAdmin = createUser.bind(null, "Система", "admin");
console.log(createUser.length);
// 3
console.log(createAdmin.length);
// 1У createAdmin уже зафіксовано два з трьох аргументів, тому залишається один необов’язковий аргумент.
Ця властивість рідко потрібна в прикладному коді, але може бути корисною під час роботи з абстракціями та бібліотеками.
bind одразу виконає функціюНеправильно:
const result = someFunction.bind(context);result — це функція, а не результат її виконання.
Правильно:
const boundFunction = someFunction.bind(context);
const result = boundFunction();bind без збереження результатуbutton.addEventListener("click", counter.increment.bind(counter));Цей код може працювати, але під час видалення обробника виникне проблема:
button.removeEventListener("click", counter.increment.bind(counter));Два виклики bind створюють дві різні функції. Обробник не буде видалений.
Краще зберегти прив’язану функцію:
const boundIncrement = counter.increment.bind(counter);
button.addEventListener("click", boundIncrement);
button.removeEventListener("click", boundIncrement);this у стрілочної функціїconst getName = () => this.name;
const boundGetName = getName.bind(user);bind не змінить this стрілочної функції. Якщо потрібен динамічний контекст, використовуйте звичайну функцію:
function getName() {
return this.name;
}
const boundGetName = getName.bind(user);bind і частковим застосуваннямbind може використовуватися для часткового застосування, але його основне призначення — фіксація this.
const boundFunction = functionName.bind(context);Якщо контекст не потрібен, як thisArg зазвичай передають null або undefined:
const addFive = add.bind(null, 5);Для складнішої логіки часткового застосування іноді зручніше написати власну обгортку або використати функцію вищого порядку.
bind створює нову функцію із зафіксованим значенням this.
Оригінальна функція не змінюється.
bind не викликає функцію одразу.
Додаткові аргументи після thisArg фіксуються заздалегідь.
Часткове застосування дає змогу створювати спеціалізовані функції з меншою кількістю параметрів.
bind часто використовують для методів класів, обробників подій і callback-функцій.
call і apply викликають функцію одразу, а bind повертає функцію для майбутнього виклику.
bind не змінює this стрілочної функції.
Для коректного видалення обробника події потрібно зберігати посилання на прив’язану функцію.