В классовом React-компоненте вы передаёте метод в обработчик события: <button onClick={this.handleClick}>. Но при вызове this теряется — метод «отцепляется» от объекта. Решение: bind. В старом коде вы встретите call и apply — они позволяют вызвать функцию с явно указанным this.
Все три метода управляют значением this при вызове функции. Разница в том, когда и как:
call — вызывает сейчас, аргументы перечисленыapply — вызывает сейчас, аргументы массивомbind — создаёт новую функцию с привязанным this, вызов позжеfunction greet(greeting, punctuation) {
return `${greeting}, ${this.name}${punctuation}`
}
const user = { name: 'Алексей' }
// Первый аргумент — this, остальные — аргументы функции
greet.call(user, 'Привет', '!') // 'Привет, Алексей!'
greet.call(user, 'Hello', '.') // 'Hello, Алексей.'greet.apply(user, ['Привет', '!']) // 'Привет, Алексей!'
// Практический кейс: Math.max с массивом (до появления spread)
const nums = [3, 1, 4, 1, 5, 9]
Math.max.apply(null, nums) // 9
// Сейчас лучше: Math.max(...nums)const boundGreet = greet.bind(user, 'Привет') // привязали this И первый аргумент
boundGreet('!') // 'Привет, Алексей!'
boundGreet('...') // 'Привет, Алексей...'
// Классический React-паттерн:
class Button {
constructor(label) {
this.label = label
this.handleClick = this.handleClick.bind(this) // привязка в конструкторе
}
handleClick() {
console.log(`Нажата: ${this.label}`)
}
}bind позволяет «зафиксировать» часть аргументов:
function multiply(a, b) {
return a * b
}
const double = multiply.bind(null, 2) // a = 2, b — потом
const triple = multiply.bind(null, 3)
console.log(double(5)) // 10
console.log(triple(5)) // 15
console.log(double(10)) // 20
// Полезный пример — логгер с уровнем:
function log(level, message) {
console.log(`[${level}] ${message}`)
}
const info = log.bind(null, 'INFO')
const error = log.bind(null, 'ERROR')
const warn = log.bind(null, 'WARN')
info('Запрос получен') // [INFO] Запрос получен
error('База недоступна') // [ERROR] База недоступнаcall/apply позволяют использовать методы одного объекта для другого:
// Массивоподобный объект — есть length и индексы, но нет методов Array
const nodeList = { 0: 'a', 1: 'b', 2: 'c', length: 3 }
// Заимствуем slice у Array.prototype:
const arr = Array.prototype.slice.call(nodeList)
console.log(arr) // ['a', 'b', 'c']
// Современный способ:
const arr2 = Array.from(nodeList) // лучшеОшибка 1: bind стрелочных функций
// Стрелочные функции нельзя привязать — bind игнорируется:
const arrow = () => this
const bound = arrow.bind({ name: 'Тест' })
bound() // this всё равно из внешнего контекста, не { name: 'Тест' }
// Bind работает только с обычными функциями и методами классовОшибка 2: лишний вызов bind в render
// Сломано (React) — создаётся новая функция при каждом рендере:
render() {
return <button onClick={this.handleClick.bind(this)}>
}
// Исправлено — привязать в конструкторе или использовать стрелочный метод:
class MyComponent {
handleClick = () => { ... } // стрелочный метод — this привязан автоматически
}Ошибка 3: call с неправильным this
// Если первый аргумент null или undefined в strict mode — this = undefined
function show() { console.log(this) }
show.call(null) // null (или global в sloppy mode)
show.call(undefined) // undefined (или global)this.method = this.method.bind(this) в конструктореelement.addEventListener('click', handler.bind(this))const boundLog = console.log.bind(console) — чтобы передавать как коллбэкspy.call(context, args) в тестах с мокамиsetTimeout(this.update.bind(this), 1000) — сохранить контекстУправление this в системе логирования
// Система логирования — демонстрация call, apply, bind
const logger = {
prefix: '[App]',
level: 'INFO',
log(message, ...extra) {
const timestamp = new Date().toISOString().slice(11, 19)
const extras = extra.length ? ' ' + JSON.stringify(extra) : ''
console.log(`${this.prefix} [${this.level}] ${timestamp}: ${message}${extras}`)
}
}
const dbLogger = { prefix: '[DB]', level: 'DEBUG' }
const authLogger = { prefix: '[Auth]', level: 'WARN' }
// call — вызвать чужой метод с другим this
logger.log.call(dbLogger, 'Подключение к базе')
// [DB] [DEBUG] 10:30:45: Подключение к базе
// apply — то же, аргументы массивом
const args = ['Ошибка токена', { userId: 42, reason: 'expired' }]
logger.log.apply(authLogger, args)
// [Auth] [WARN] 10:30:45: Ошибка токена [{"userId":42,"reason":"expired"}]
// bind — создать специализированный логгер
const errorLogger = { prefix: '[Error]', level: 'ERROR' }
const logError = logger.log.bind(errorLogger)
logError('Сервер недоступен') // [Error] [ERROR] 10:30:45: Сервер недоступен
logError('Таймаут', { ms: 5000 }) // [Error] [ERROR] 10:30:45: Таймаут [{"ms":5000}]
// Частичное применение — зафиксировать уровень
function createTaggedLog(level, message, ...extra) {
console.log(`[${level}] ${message}`, ...extra)
}
const debugLog = createTaggedLog.bind(null, 'DEBUG')
const errorLog = createTaggedLog.bind(null, 'ERROR')
debugLog('Загрузка модуля', { name: 'auth' })
// [DEBUG] Загрузка модуля { name: 'auth' }
errorLog('Не удалось сохранить')
// [ERROR] Не удалось сохранить
// Заимствование: взять toString у Array для объекта
const cart = { 0: 'Ноутбук', 1: 'Мышь', 2: 'Коврик', length: 3 }
const items = Array.prototype.slice.call(cart)
console.log(items) // ['Ноутбук', 'Мышь', 'Коврик']
console.log(items.join(', ')) // 'Ноутбук, Мышь, Коврик'В классовом React-компоненте вы передаёте метод в обработчик события: <button onClick={this.handleClick}>. Но при вызове this теряется — метод «отцепляется» от объекта. Решение: bind. В старом коде вы встретите call и apply — они позволяют вызвать функцию с явно указанным this.
Все три метода управляют значением this при вызове функции. Разница в том, когда и как:
call — вызывает сейчас, аргументы перечисленыapply — вызывает сейчас, аргументы массивомbind — создаёт новую функцию с привязанным this, вызов позжеfunction greet(greeting, punctuation) {
return `${greeting}, ${this.name}${punctuation}`
}
const user = { name: 'Алексей' }
// Первый аргумент — this, остальные — аргументы функции
greet.call(user, 'Привет', '!') // 'Привет, Алексей!'
greet.call(user, 'Hello', '.') // 'Hello, Алексей.'greet.apply(user, ['Привет', '!']) // 'Привет, Алексей!'
// Практический кейс: Math.max с массивом (до появления spread)
const nums = [3, 1, 4, 1, 5, 9]
Math.max.apply(null, nums) // 9
// Сейчас лучше: Math.max(...nums)const boundGreet = greet.bind(user, 'Привет') // привязали this И первый аргумент
boundGreet('!') // 'Привет, Алексей!'
boundGreet('...') // 'Привет, Алексей...'
// Классический React-паттерн:
class Button {
constructor(label) {
this.label = label
this.handleClick = this.handleClick.bind(this) // привязка в конструкторе
}
handleClick() {
console.log(`Нажата: ${this.label}`)
}
}bind позволяет «зафиксировать» часть аргументов:
function multiply(a, b) {
return a * b
}
const double = multiply.bind(null, 2) // a = 2, b — потом
const triple = multiply.bind(null, 3)
console.log(double(5)) // 10
console.log(triple(5)) // 15
console.log(double(10)) // 20
// Полезный пример — логгер с уровнем:
function log(level, message) {
console.log(`[${level}] ${message}`)
}
const info = log.bind(null, 'INFO')
const error = log.bind(null, 'ERROR')
const warn = log.bind(null, 'WARN')
info('Запрос получен') // [INFO] Запрос получен
error('База недоступна') // [ERROR] База недоступнаcall/apply позволяют использовать методы одного объекта для другого:
// Массивоподобный объект — есть length и индексы, но нет методов Array
const nodeList = { 0: 'a', 1: 'b', 2: 'c', length: 3 }
// Заимствуем slice у Array.prototype:
const arr = Array.prototype.slice.call(nodeList)
console.log(arr) // ['a', 'b', 'c']
// Современный способ:
const arr2 = Array.from(nodeList) // лучшеОшибка 1: bind стрелочных функций
// Стрелочные функции нельзя привязать — bind игнорируется:
const arrow = () => this
const bound = arrow.bind({ name: 'Тест' })
bound() // this всё равно из внешнего контекста, не { name: 'Тест' }
// Bind работает только с обычными функциями и методами классовОшибка 2: лишний вызов bind в render
// Сломано (React) — создаётся новая функция при каждом рендере:
render() {
return <button onClick={this.handleClick.bind(this)}>
}
// Исправлено — привязать в конструкторе или использовать стрелочный метод:
class MyComponent {
handleClick = () => { ... } // стрелочный метод — this привязан автоматически
}Ошибка 3: call с неправильным this
// Если первый аргумент null или undefined в strict mode — this = undefined
function show() { console.log(this) }
show.call(null) // null (или global в sloppy mode)
show.call(undefined) // undefined (или global)this.method = this.method.bind(this) в конструктореelement.addEventListener('click', handler.bind(this))const boundLog = console.log.bind(console) — чтобы передавать как коллбэкspy.call(context, args) в тестах с мокамиsetTimeout(this.update.bind(this), 1000) — сохранить контекстУправление this в системе логирования
// Система логирования — демонстрация call, apply, bind
const logger = {
prefix: '[App]',
level: 'INFO',
log(message, ...extra) {
const timestamp = new Date().toISOString().slice(11, 19)
const extras = extra.length ? ' ' + JSON.stringify(extra) : ''
console.log(`${this.prefix} [${this.level}] ${timestamp}: ${message}${extras}`)
}
}
const dbLogger = { prefix: '[DB]', level: 'DEBUG' }
const authLogger = { prefix: '[Auth]', level: 'WARN' }
// call — вызвать чужой метод с другим this
logger.log.call(dbLogger, 'Подключение к базе')
// [DB] [DEBUG] 10:30:45: Подключение к базе
// apply — то же, аргументы массивом
const args = ['Ошибка токена', { userId: 42, reason: 'expired' }]
logger.log.apply(authLogger, args)
// [Auth] [WARN] 10:30:45: Ошибка токена [{"userId":42,"reason":"expired"}]
// bind — создать специализированный логгер
const errorLogger = { prefix: '[Error]', level: 'ERROR' }
const logError = logger.log.bind(errorLogger)
logError('Сервер недоступен') // [Error] [ERROR] 10:30:45: Сервер недоступен
logError('Таймаут', { ms: 5000 }) // [Error] [ERROR] 10:30:45: Таймаут [{"ms":5000}]
// Частичное применение — зафиксировать уровень
function createTaggedLog(level, message, ...extra) {
console.log(`[${level}] ${message}`, ...extra)
}
const debugLog = createTaggedLog.bind(null, 'DEBUG')
const errorLog = createTaggedLog.bind(null, 'ERROR')
debugLog('Загрузка модуля', { name: 'auth' })
// [DEBUG] Загрузка модуля { name: 'auth' }
errorLog('Не удалось сохранить')
// [ERROR] Не удалось сохранить
// Заимствование: взять toString у Array для объекта
const cart = { 0: 'Ноутбук', 1: 'Мышь', 2: 'Коврик', length: 3 }
const items = Array.prototype.slice.call(cart)
console.log(items) // ['Ноутбук', 'Мышь', 'Коврик']
console.log(items.join(', ')) // 'Ноутбук, Мышь, Коврик'В интернет-магазине есть объект formatter с методами formatPrice(price, currency) и formatCount(n, unit). Методы используют this.locale для локализации. Создай специализированные функции: formatRub (форматирует цену в рублях для locale 'ru-RU'), formatUsd (в долларах для 'en-US'), formatItems (количество со словом 'шт'). Используй bind для привязки this и частичного применения аргументов.
formatRub = formatter.formatPrice.bind(ruFormatter, — нет, нужно bind только this и добавить currency как второй аргумент частично). Попробуй: formatter.formatPrice.bind(ruFormatter) и вызов formatRub(price, "RUB"). Или частично: bind(ruFormatter, 1499.99) нет — bind(ruFormatter) создаёт функцию, которой передаёшь price и "RUB". Для одного аргумента: bind(usFormatter, — нет. Используй bind(context) для this, потом фиксируй аргументы.