Bind js что это
Мы хотим сделать этот проект с открытым исходным кодом доступным для людей во всем мире. Пожалуйста, помогите нам перевести это руководство на другие языки.
Светлая тема Тёмная тема
Поделиться
- Учебник
- Язык JavaScript
- Продвинутая работа с функциями
- Привязка контекста к функции
Повторный bind
важность: 5
Можем ли мы изменить this дополнительным связыванием?
Что выведет этот код?
function f() < alert(this.name); >f = f.bind( ).bind( ); f();
Ответ: Вася.
function f() < alert(this.name); >f = f.bind( ).bind( ); f(); // Вася
Экзотический объект bound function, возвращаемый при первом вызове f.bind(. ) , запоминает контекст (и аргументы, если они были переданы) только во время создания.
Следующий вызов bind будет устанавливать контекст уже для этого объекта. Это ни на что не повлияет.
Можно сделать новую привязку, но нельзя изменить существующую.
Метод bind
Метод bind привязать контекст к функции. В качестве первого параметра следует передавать контекст, а последующими параметрами — параметры функции. Метод возвращает новую функцию, внутри которой this будет равным переданному контексту.
Синтаксис
функция.bind(контекст, параметр1, параметр2. );
Пример
Пусть у нас есть инпут:
Пусть ссылка на этот инпут записана в переменную elem :
let elem = document.querySelector(‘#elem’);
Пусть у нас также есть следующая функция func :
function func(param1, param2) < console.log(this.value + param1 + param2); >
Давайте с помощью bind сделаем новую функцию, которая будет копией функции func , но this в ней всегда будет равен elem :
let newFunc = func.bind(elem);
Теперь в переменной newFunc лежит функция. Давайте вызовем ее, передав в первый параметр ‘1’ , а во второй ‘2’ :
newFunc(‘1’, ‘2’);
Давайте соберем все вместе:
let elem = document.getElementById(‘elem’); function func(param1, param2) < console.log(this.value + param1 + param2); >let newFunc = func.bind(elem); newFunc(‘1’, ‘2’); // выведет ‘text12’
Пример
Не обязательно записывать результат работы bind в новую функцию newFunc , можно просто перезаписать func :
let func = func.bind(elem);
Смотрите также
- метод call ,
который вызывает функцию с контекстом - метод apply ,
который вызывает функцию с контекстом
Как работают this, call, apply и bind в Java Script: разбираемся на примерах
Привет, Хабр! Эту статью написал Тарас Голомозый, fullstack web-разработчик и преподаватель в школе программирования Эльбрус Буткемп. В своей практике он часто сталкивается с кейсами выпускников, которых на собеседовании просят рассказать о роли ключевого слова this в JavaScript. Простого определения про ссылку на контекст часто оказывается недостаточно, требуется более глубокое погружение в тему. В этой статье он на нескольких примерах разбирает, в каких ситуациях может пригодиться это ключевое слово и как используется call, apply и bind.

В общем смысле this — это ссылка на определенный контекст внутри объекта. Рассмотрим, что это означает, на примере.
Создадим объект car с набором свойств: моделью и годом выпуска автомобиля.
const car =
Для того, чтобы обратиться к объекту извне, достаточно выбрать имя переменной и через точку выбрать любое свойство: например, model .
console.log (car.model)
В консоли мы увидим модель автомобиля: Toyota.
Обратиться к объекту изнутри сложнее: находясь внутри фигурных скобок, мы не знаем, куда будет записан объект и будет ли записан вообще. Попробуем сделать это, создав внутри объекта функцию showModel . Ее единственная задача — показывать модель автомобиля:
const car = < model: 'Toyota', year: 2007, showModel: function(model, year)< console.log(this.model); >>;
Чтобы из этой функции получить доступ к model , пишем this.model . Благодаря ключевому слову this можно получить доступ к любому свойству объекта, находясь внутри него.
Теперь из объекта car можно вызвать showModel — метод покажет модель автомобиля, Toyota.
Допустим, у нас есть еще один автомобиль. Создадим новый объект и назовем его anotherCar . У этого объекта будут те же свойства: model и year .
const anotherCar = < model: 'Benz', year: 1998 showModel: function(model, year)< console.log(this.model); >>;
Если скопировать функцию showModel с this внутрь нового объекта, то при вызове anotherCar.showModel , то в консоли мы увидим модель той машины, внутри которой вызывается этот метод. В данном случае — Benz.
Исходя из этих примеров, можно сказать, что this — это ссылка на объект, внутри которого находится это ключевое слово.
Важно отметить, что функции бывают обычными (как в этом примере) и стрелочными. Если в этом примере заменить функцию на стрелочную, в консоли мы увидим undefined. Дело в том, что стрелочные функции не имеют своего this.
Call, apply и bind
Что делать, если у второго автомобиля нет метода, который показывает его модель? Можем ли мы использовать функцию с вызовом модели первого автомобиля? Да, для этого в JavaScript есть специальные ключевые слова: call, apply и bind.
Начнем с call. Вызываем из первой машины метод car.showModel , используя call. В качестве аргумента ключевого слова указываем название объекта, в который записан второй автомобиль, anotherCar . В консоли мы увидим Benz.
То же самое можно сделать с помощью метода apply, просто меняя ключевое слово: car.showModel.apply(anotherCar); . Результат будет таким же: в консоли мы увидим Benz.
Возникает вопрос — чем call отличается от apply? Разница между ними в формате передачи параметров. Рассмотрим пример: функция showModel , кроме вывода модели автомобиля, может принимать еще какие-то параметры: например, цвет автомобиля и тип двигателя.
const car = < model: 'Toyota', year: 2007, showModel: function(color, engine)< console.log(this.model, color, engine); >>;
Используя apply, новые параметры можно передать в виде массива: например, автомобиль будет красным и с бензиновым двигателем:
// car.showModel.apply(anotherCar, ['red', 'diesel']);
В консоли мы увидим, что это красный дизельный Benz.
Call позволяет передать те же параметры не в виде массива, а простым перечислением: // car.showModel.call(anotherCar, ‘green’, ‘gas’) .
Третий метод, bind, позволяет создать новую функцию и записать ее в переменную. Выглядеть это будет так:
const modelShower = car.showModel.bind(anotherCar); modelShower('black', 'diesel');
Функция modelShower вызывает метод showModel, а bind привязывает к нему контекст другого автомобиля — в нашем случае это anotherCar.Таким образом, в переменной modelShower появилась функция, способная показывать контекст anotherCar так, будто она написана внутри самого anotherCar.
Передадим в функцию цвет и параметры двигателя:
modelShower('black', 'diesel');
В выводе мы, как и ожидалось, получим черный дизельный Benz.
Заключение
Рады, если теперь вам удалось разобраться, что такое this в JavaScript, в чем разница между call и apply и для чего нужен bind. Если у вас появились вопросы или уточнения, пишите их в комментариях!
Привязка контекста к функции
При передаче методов объекта в качестве колбэков, например для setTimeout , возникает известная проблема – потеря this .
В этой главе мы посмотрим, как её можно решить.
Потеря «this»
Мы уже видели примеры потери this . Как только метод передаётся отдельно от объекта – this теряется.
Вот как это может произойти в случае с setTimeout :
let user = < firstName: "Вася", sayHi() < alert(`Привет, $!`); > >; setTimeout(user.sayHi, 1000); // Привет, undefined!
При запуске этого кода мы видим, что вызов this.firstName возвращает не «Вася», а undefined !
Это произошло потому, что setTimeout получил функцию sayHi отдельно от объекта user (именно здесь функция и потеряла контекст). То есть последняя строка может быть переписана как:
let f = user.sayHi; setTimeout(f, 1000); // контекст user потеряли
Метод setTimeout в браузере имеет особенность: он устанавливает this=window для вызова функции (в Node.js this становится объектом таймера, но здесь это не имеет значения). Таким образом, для this.firstName он пытается получить window.firstName , которого не существует. В других подобных случаях this обычно просто становится undefined .
Задача довольно типичная – мы хотим передать метод объекта куда-то ещё (в этом конкретном случае – в планировщик), где он будет вызван. Как бы сделать так, чтобы он вызывался в правильном контексте?
Решение 1: сделать функцию-обёртку
Самый простой вариант решения – это обернуть вызов в анонимную функцию, создав замыкание:
let user = < firstName: "Вася", sayHi() < alert(`Привет, $!`); > >; setTimeout(function() < user.sayHi(); // Привет, Вася! >, 1000);
Теперь код работает корректно, так как объект user достаётся из замыкания, а затем вызывается его метод sayHi .
То же самое, только короче:
setTimeout(() => user.sayHi(), 1000); // Привет, Вася!
Выглядит хорошо, но теперь в нашем коде появилась небольшая уязвимость.
Что произойдёт, если до момента срабатывания setTimeout (ведь задержка составляет целую секунду!) в переменную user будет записано другое значение? Тогда вызов неожиданно будет совсем не тот!
let user = < firstName: "Вася", sayHi() < alert(`Привет, $!`); > >; setTimeout(() => user.sayHi(), 1000); // . в течение 1 секунды user = < sayHi() < alert("Другой пользователь в 'setTimeout'!"); >>; // Другой пользователь в 'setTimeout'!
Следующее решение гарантирует, что такого не случится.
Решение 2: привязать контекст с помощью bind
В современном JavaScript у функций есть встроенный метод bind, который позволяет зафиксировать this .
Базовый синтаксис bind :
// полный синтаксис будет представлен немного позже let boundFunc = func.bind(context);
Результатом вызова func.bind(context) является особый «экзотический объект» (термин взят из спецификации), который вызывается как функция и прозрачно передаёт вызов в func , при этом устанавливая this=context .
Другими словами, вызов boundFunc подобен вызову func с фиксированным this .
Например, здесь funcUser передаёт вызов в func , фиксируя this=user :
let user = < firstName: "Вася" >; function func() < alert(this.firstName); >let funcUser = func.bind(user); funcUser(); // Вася
Здесь func.bind(user) – это «связанный вариант» func , с фиксированным this=user .
Все аргументы передаются исходному методу func как есть, например:
let user = < firstName: "Вася" >; function func(phrase) < alert(phrase + ', ' + this.firstName); >// привязка this к user let funcUser = func.bind(user); funcUser("Привет"); // Привет, Вася (аргумент "Привет" передан, при этом this = user)
Теперь давайте попробуем с методом объекта:
let user = < firstName: "Вася", sayHi() < alert(`Привет, $!`); > >; let sayHi = user.sayHi.bind(user); // (*) sayHi(); // Привет, Вася! setTimeout(sayHi, 1000); // Привет, Вася!
В строке (*) мы берём метод user.sayHi и привязываем его к user . Теперь sayHi – это «связанная» функция, которая может быть вызвана отдельно или передана в setTimeout (контекст всегда будет правильным).
Здесь мы можем увидеть, что bind исправляет только this , а аргументы передаются как есть:
let user = < firstName: "Вася", say(phrase) < alert(`$, $!`); > >; let say = user.say.bind(user); say("Привет"); // Привет, Вася (аргумент "Привет" передан в функцию "say") say("Пока"); // Пока, Вася (аргумент "Пока" передан в функцию "say")
Удобный метод: bindAll
Если у объекта много методов и мы планируем их активно передавать, то можно привязать контекст для них всех в цикле:
for (let key in user) < if (typeof user[key] == 'function') < user[key] = user[key].bind(user); >>
Некоторые JS-библиотеки предоставляют встроенные функции для удобной массовой привязки контекста, например _.bindAll(obj) в lodash.
Частичное применение
До сих пор мы говорили только о привязывании this . Давайте шагнём дальше.
Мы можем привязать не только this , но и аргументы. Это делается редко, но иногда может быть полезно.
Полный синтаксис bind :
let bound = func.bind(context, [arg1], [arg2], . );
Это позволяет привязать контекст this и начальные аргументы функции.
Например, у нас есть функция умножения mul(a, b) :
function mul(a, b)
Давайте воспользуемся bind , чтобы создать функцию double на её основе:
function mul(a, b) < return a * b; >let double = mul.bind(null, 2); alert( double(3) ); // = mul(2, 3) = 6 alert( double(4) ); // = mul(2, 4) = 8 alert( double(5) ); // = mul(2, 5) = 10
Вызов mul.bind(null, 2) создаёт новую функцию double , которая передаёт вызов mul , фиксируя null как контекст, и 2 – как первый аргумент. Следующие аргументы передаются как есть.
Это называется частичное применение – мы создаём новую функцию, фиксируя некоторые из существующих параметров.
Обратите внимание, что в данном случае мы на самом деле не используем this . Но для bind это обязательный параметр, так что мы должны передать туда что-нибудь вроде null .
В следующем коде функция triple умножает значение на три:
function mul(a, b) < return a * b; >let triple = mul.bind(null, 3); alert( triple(3) ); // = mul(3, 3) = 9 alert( triple(4) ); // = mul(3, 4) = 12 alert( triple(5) ); // = mul(3, 5) = 15
Для чего мы обычно создаём частично применённую функцию?
Польза от этого в том, что возможно создать независимую функцию с понятным названием ( double , triple ). Мы можем использовать её и не передавать каждый раз первый аргумент, т.к. он зафиксирован с помощью bind .
В других случаях частичное применение полезно, когда у нас есть очень общая функция и для удобства мы хотим создать её более специализированный вариант.
Например, у нас есть функция send(from, to, text) . Потом внутри объекта user мы можем захотеть использовать её частный вариант: sendTo(to, text) , который отправляет текст от имени текущего пользователя.
Частичное применение без контекста
Что если мы хотим зафиксировать некоторые аргументы, но не контекст this ? Например, для метода объекта.
Встроенный bind не позволяет этого. Мы не можем просто опустить контекст и перейти к аргументам.
К счастью, легко создать вспомогательную функцию partial , которая привязывает только аргументы.
function partial(func, . argsBound) < return function(. args) < // (*) return func.call(this, . argsBound, . args); >> // использование: let user = < firstName: "John", say(time, phrase) < alert(`[$] $: $!`); > >; // добавляем частично применённый метод с фиксированным временем user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes()); user.sayNow("Hello"); // Что-то вроде этого: // [10:00] John: Hello!
Результатом вызова partial(func[, arg1, arg2. ]) будет обёртка (*) , которая вызывает func с:
- Тем же this , который она получает (для вызова user.sayNow – это будет user )
- Затем передаёт ей . argsBound – аргументы из вызова partial ( «10:00» )
- Затем передаёт ей . args – аргументы, полученные обёрткой ( «Hello» )
Благодаря оператору расширения . реализовать это очень легко, не правда ли?
Также есть готовый вариант _.partial из библиотеки lodash.
Итого
Метод bind возвращает «привязанный вариант» функции func , фиксируя контекст this и первые аргументы arg1 , arg2 …, если они заданы.
Обычно bind применяется для фиксации this в методе объекта, чтобы передать его в качестве колбэка. Например, для setTimeout .
Когда мы привязываем аргументы, такая функция называется «частично применённой» или «частичной».
Частичное применение удобно, когда мы не хотим повторять один и тот же аргумент много раз. Например, если у нас есть функция send(from, to) и from всё время будет одинаков для нашей задачи, то мы можем создать частично применённую функцию и дальше работать с ней.