Что-то интересное в JavaScript — это то, как он обрабатывает функции. В JavaScript функции — это первоклассные данные, и их можно передавать так же, как и примитивные данные, такие как строки или массивы. Функции могут быть параметром другой функции, храниться в массиве или быть доступными из ключа объекта. Но при передаче функций по их контексту иногда можно потеряться. Вот тут-то и появляется метод bind()
.
Метод bind()
в самом простом случае имеет следующий формат:
let boundFunc = func.bind(thisArg)
Параметр thisArg
— это значение, которое будет использоваться в качестве параметра this
в функции func
при вызове функции. Другими словами, когда мы вызываем boundFunc
, мы хотим использовать func
в контексте thisArg
.
Теперь для примера предположим, что у вас есть объект друга:
let friend = { name: "John", greetFriend: function(greeting = "Hello") { console.log(`${greeting}, ${this.name}`) } } friend.greetFriend() => Hello, John
Если вы вызовете функцию greetFriend
немедленно, она будет работать, как мы и ожидали, записывая в журнал приветствие, которое мы ввели, и name
объекта friend
. Но если бы мы хотели сохранить функцию в новой переменной, это не сработало бы так же:
let greetJohn = friend.greetFriend greetJohn("Good morning") => Good morning, undefined
В данном случае, потому что мы не вызываем функцию по отношению к объекту friend
. Без контекста friend
this
в методе greetJohn
пытается найти переменную с именем name
в своем собственном контексте, в данном случае в глобальной области видимости. Вот тут-то и появляется bind()
. Мы передаем в качестве аргумента bind
контекст, в котором мы хотим использовать this
. Вот так:
let greetJohn = friend.greetFriend.bind(friend) greetJohn("Good evening") => Good evening, John
Мы также можем использовать это для привязки функции к контексту, отличному от того, из которого она пришла:
let newFriend = { name: "Jenny" } let greetJenny = friend.greetFriend.bind(newFriend) greetJenny("Good afternoon") => Good afternoon, Jenny
Одним из наиболее важных способов использования bind
является передача функции обратного вызова другой функции. Итак, если, например, мы хотим вызвать метод greetFriend
для ведения журнала через две секунды, мы могли бы подумать, что можем использовать функцию setTimeout
следующим образом:
setTimeout(friend.greetFriend, 2000)
Но, к сожалению, обратный вызов не сохраняет контекст, из которого он пришел. Приведенная выше строка соответствует:
let f = friend.greetFriend
setTimeout(f, 2000)
=> Hello, undefined
Один из способов обойти это — использовать функцию-оболочку:
setTimeout(() => friend.greetFriend(), 2000)
=> Hello, John
Но есть один возможный недостаток, который может возникнуть в этом случае. Если в течение двух секунд до запуска метода ключ greetFriend
в friend
будет изменен, метод setTimeout
вызовет новый метод. Если вы используете bind
, setTimeout
будет использовать предварительно связанное значение, поэтому этот способ безопаснее:
setTimeout(friend.greetFriend.bind(friend, "Hey there"), 2000)
=> Hey there, John
Как вы можете видеть выше, метод bind
также может принимать больше, чем просто параметр thisArg
. Он также может принимать необязательные аргументы для подключения к функции, которую вы привязываете к thisArg
. Таким образом, мы могли бы использовать bind
в методе greetFriend
по-другому:
let hiJohn = friend.greetFriend.bind(friend, "Hi") hiJohn() => "Hi, John"
Другой способ использования необязательных аргументов bind
— использовать их в функции, которой не нужен контекст, например:
function multiply(num1, num2) { return num1 * num2 } let double = multiply.bind(null, 2) double(5) => 10
В этом случае вызов bind
создает новую функцию double
, которая вызывает умножение. Поскольку нам не нужен никакой контекст для функции multiply
, так как она не использует this
, мы передаем null
в качестве первого параметра bind
. Поскольку мы зафиксировали 2
в качестве первого аргумента, double примет только один аргумент и передаст его для умножения на место num2
.
В конечном счете, bind()
— хороший метод для знакомства с JavaScript. Учитывая тот факт, что JavaScript использует функции так часто и по-разному, есть много возможностей для потери контекста функции. Если вы знаете, как правильно использовать bind()
, вы всегда можете убедиться, что функции используются в том контексте, в котором вы их предполагаете.