Другое

Когда использовать JavaScript bind() vs call() vs apply()

Узнайте, когда использовать метод JavaScript bind() по сравнению с call() и apply(). Узнайте о ключевых различиях в поведении выполнения, идеальных случаях использования для обратных вызовов, обработчиков событий и частичного применения функций. Освойте управление контекстом функции с практическими примерами.

Когда следует использовать метод JavaScript bind() по сравнению с call() и apply()?

Я уже знаю, что apply() и call() — это похожие функции, которые устанавливают контекст this для функции. Разница заключается в способе передачи аргументов (ручной vs массив).

Но когда следует использовать метод bind()?

javascript
var obj = {
  x: 81,
  getX: function() {
    return this.x;
  }
};

alert(obj.getX.bind(obj)());
alert(obj.getX.call(obj));
alert(obj.getX.apply(obj));

Метод JavaScript bind() следует использовать, когда нужно создать новую функцию с постоянно привязанным контекстом this, особенно для асинхронных колбэков, обработчиков событий или когда необходимо сохранять контекст объекта между вызовами функций. В отличие от call() и apply(), которые немедленно выполняют функции с определенным значением this, bind() возвращает новую функцию, которая сохраняет контекст до момента вызова, что делает его идеальным для сценариев, где нужно передавать ссылки на функции, сохраняя правильный контекст объекта.

Содержание

Чем bind() отличается от call() и apply()?

Основное различие между bind() и двумя другими методами заключается в их поведении выполнения. В то время как call() и apply() немедленно вызывают функцию с указанным контекстом this, bind() создает и возвращает новую функцию, которую можно вызвать позже с постоянно привязанным контекстом.

Согласно GeeksforGeeks, “bind() работает немного иначе. В отличие от call() и apply(), которые немедленно выполняют текущую функцию, bind() возвращает новую функцию”. Это различие важно - bind() не выполняет функцию немедленно, позволяя создавать предварительно настроенные функции для последующего использования.

Ключевые различия:

  • bind(): Возвращает новую функцию с привязанным контекстом
  • call(): Немедленно выполняет с аргументами, разделенными запятыми
  • apply(): Немедленно выполняет с массивом аргументов

Когда использовать bind() для колбэков

Одним из наиболее распространенных случаев использования bind() является сохранение контекста this в асинхронных колбэках. Когда вы передаете метод как колбэк функциям таким как setTimeout(), setInterval() или обработчикам событий, исходный контекст this часто теряется.

Как демонстрирует JavaScript Tutorial, “Когда вы передаете метод объекта другой функции в качестве колбэка, this теряется.”

javascript
let person = { 
  name: 'John Doe', 
  getName: function() { 
    console.log(this.name); 
  } 
};

// Без bind - this будет undefined или window
setTimeout(person.getName, 1000); // Проблема!

// С bind - контекст this сохраняется
setTimeout(person.getName.bind(person), 1000); // Работает правильно!

Этот шаблон необходим для:

  • Обработчиков событий при манипуляции DOM
  • Колбэков в промисах и async/await
  • Функций таймера (setTimeout, setInterval)
  • Массивов, принимающих колбэки (map, filter, forEach)

Частичное применение функций с помощью bind()

bind() позволяет создавать частично примененные функции, фиксируя некоторые аргументы, в то время как другие оставляются открытыми для последующего указания. Это особенно полезно, когда у вас есть универсальные функции, которым нужны конкретные варианты для повторного использования.

JavaScript.info объясняет: “В других случаях частичное применение полезно, когда у нас есть очень универсальная функция, и мы хотим менее универсальный вариант для удобства.”

javascript
// Универсальная функция
function send(from, to, text) {
  console.log(`От: ${from}, Кому: ${to}, Сообщение: ${text}`);
}

// Создаем частично примененную функцию
const sendFromCurrentUser = send.bind(currentUser);

// Теперь можно вызывать с оставшимися аргументами
sendFromCurrentUser('Alice', 'Привет!');
sendFromCurrentUser('Bob', 'Как дела?');

Этот метод ценен для:

  • Создания специализированных версий универсальных функций
  • Сокращения шаблонного кода в повторяющихся операциях
  • Предварительной настройки API-вызовов с фиксированными параметрами
  • Настройки утилитарных функций со значениями по умолчанию

Сохранение контекста объекта в обработчиках событий

В событийно-ориентированном программировании, особенно при работе с классами или объектно-ориентированными паттернами, bind() гарантирует, что методы обработчиков событий сохраняют правильный контекст this при срабатывании от DOM или других систем событий.

Как отмечено на Stack Overflow, “Это наиболее распространено, когда ваша функция на самом деле является методом, и вы хотите, чтобы значение this было установлено на определенный объект, чтобы метод работал с этим конкретным объектом.”

javascript
class Button {
  constructor(element) {
    this.element = element;
    this.count = 0;
    
    // Без bind: this будет undefined при клике
    // this.element.addEventListener('click', this.handleClick);
    
    // С bind: контекст this сохраняется
    this.element.addEventListener('click', this.handleClick.bind(this));
  }
  
  handleClick() {
    this.count++;
    console.log(`Кнопка нажата ${this.count} раз`);
  }
}

Этот шаблон гарантирует, что:

  • Методы классов сохраняют свой контекст this в обработчиках событий
  • Свойства объекта остаются доступными во время обработки событий
  • Код остается чистым и поддерживаемым без обходных решений

Заем методов между объектами

bind() позволяет временно “занимать” методы одного объекта и использовать их с контекстом другого объекта. Это отличается от прямого заимствования методов и обеспечивает большую гибкость.

Programiz демонстрирует эту концепцию: “объект student2 заимствует метод introduction у student1.”

javascript
const student1 = {
  name: 'Alice',
  introduction: function() {
    console.log(`${this.name} учится в классе ${this.grade}`);
  }
};

const student2 = {
  name: 'Jimmy',
  grade: 6
};

// Занимаем метод с контекстом student2
const boundIntroduction = student1.introduction.bind(student2);
boundIntroduction(); // Jimmy учится в классе 6

Хотя этого можно достичь с помощью call() или apply(), bind() предпочтительнее, когда заимствованный метод должен быть повторно используемым в нескольких вызовах или событиях.

Вопросы производительности

Хотя bind() мощный, важно понимать последствия для производительности. Создание привязанных функций имеет некоторые накладные расходы по сравнению с прямым использованием call() или apply().

Согласно Vultr Docs, “Чтобы полностью понять bind, call и apply, вы должны понять концепцию JavaScript this.”

Учитывайте эти факторы производительности:

  • bind(): Создает новую функцию - более высокая начальная стоимость, но повторно используемая
  • call()/apply(): Прямое выполнение - более низкая стоимость на вызов, но нужно указывать контекст каждый раз

Для часто вызываемых функций bind() может быть более эффективным после создания привязанной функции. Для одноразовых вызовов call() или apply() могут быть более подходящими.

Практические примеры реализации

Рассмотрим несколько практических сценариев, где bind() проявляет себя лучше, чем call() и apply().

Пример 1: Обработчик событий с контекстом класса

javascript
class Timer {
  constructor() {
    this.seconds = 0;
    this.interval = null;
    
    // Привязываем метод для сохранения контекста this
    this.tick = this.tick.bind(this);
  }
  
  start() {
    this.interval = setInterval(this.tick, 1000);
  }
  
  tick() {
    this.seconds++;
    console.log(`Таймер: ${this.seconds} секунд`);
  }
  
  stop() {
    clearInterval(this.interval);
  }
}

const timer = new Timer();
timer.start(); // Работает правильно с привязанным контекстом

Пример 2: Создание повторно используемых утилитарных функций

javascript
// Утилиты для массивов с сохранением контекста
const arrayUtils = {
  max: function() {
    return Math.max.apply(null, this);
  },
  
  sum: function() {
    return this.reduce((acc, val) => acc + val, 0);
  }
};

const numbers = [1, 2, 3, 4, 5];

// Создаем специализированные функции
const maxNumbers = arrayUtils.max.bind(numbers);
const sumNumbers = arrayUtils.sum.bind(numbers);

console.log(maxNumbers()); // 5
console.log(sumNumbers()); // 15

Пример 3: Конфигурация API-запросов

javascript
const apiClient = {
  baseUrl: 'https://api.example.com',
  
  request: function(endpoint, data) {
    console.log(`Запрос к ${this.baseUrl}${endpoint} с данными:`, data);
  }
};

// Создаем предварительно настроенные методы API
const getUser = apiClient.request.bind(apiClient, '/users');
const createPost = apiClient.request.bind(apiClient, '/posts');

getUser({ id: 123 });
createPost({ title: 'Привет', content: 'Мир' });

Эти примеры демонстрируют, как bind() обеспечивает чистый и поддерживаемый подход к управлению контекстом функций, который call() и apply() не могут превзойти в сценариях, требующих повторно используемых функций с привязанным контекстом.

Заключение

Метод bind() является незаменимым инструментом JavaScript для управления контекстом функций, особенно когда нужно создавать повторно используемые функции с постоянно привязанными значениями this. Используйте bind(), когда нужно сохранять контекст объекта в колбэках, создавать частично примененные функции или поддерживать контекст методов класса в обработчиках событий. Помните, что bind() возвращает новую функцию, а не выполняет ее немедленно, что делает его идеальным для сценариев, где нужны предварительно настроенные функции для последующего использования. Для немедленного выполнения с определенными контекстами call() и apply() остаются лучшими выборами, но когда приоритетом является сохранение контекста и повторное использование, bind() является превосходным решением.

Источники

  1. Explain call(), apply(), and bind() methods in JavaScript - GeeksforGeeks
  2. JavaScript: bind() vs apply() and call() - W3Docs
  3. JavaScript bind() Method and Its Practical Applications - JavaScript Tutorial
  4. Function binding - JavaScript.info
  5. Function.prototype.bind() - MDN
  6. When to use .bind() in JS - Stack Overflow
  7. JavaScript Function bind() (With Examples) - Programiz
  8. How AND When to use bind, call, and apply in Javascript — Eigen X
  9. JavaScript Function bind() - Create Bound Function | Vultr Docs
  10. How to Use the Call, Apply, and Bind Functions in JavaScript – with Code Examples - freeCodeCamp
Авторы
Проверено модерацией
Модерация