loading...

Functional Binding in JavaScript

spukas profile image Linas Spukas ・3 min read

If you already spent some time coding with JavaScript, you should come across a bind() function. For beginners, it is not an easy concept to understand. Let's try to analyze a few simple examples and give clarification on what does it do, and how can it be applied.

Understanding Keyword this

To understand what bind() do, first we need to clarify what is this. And it differs on where it is being used.

In the global context, the keyword this will refer to the global Window object. If you open your browser console and write this, you'll see a Window object with all the properties.

console.log(this) // Window {parent: Window, opener: null, …}

In the function context, it is a little bit tricky, because it depends on how the functions are being called. If we have a simple function call, without the specified this context, by default this will be assigned to the global Window object.

function getContext() {
  return this;
}

getContext() // Window {parent: Window, opener: null, …}

If you will call a function with a methods .call() or .apply(), you can set the context to this by providing the first argument.

function getContext() {
  return this;
}

// creating custom object
const user = { age: 20 };

// calling and setting functions context to the object "user"
getContext.call(user) // { a: 20 }

If the function is being called as an object method, this keyword will refer to the object, where the method is being called on.

const user = {
  age: 20,
  getAge: function() {
    return this.age;
  },
  getContext: function() {
    return this;
  }
}

user.getAge() // 20
user.getContext() // {age: 20, getAge: ƒ, getContext: ƒ} 

Function bind() Method

Now that we recalled what keyword this means, it should be simpler to understand what problem solves bind() method.

By definition, bind() is a function prototype method that creates a new function with the same body and scope as the original one and sets a new context value from the first provided argument to the keyword this.

function getName() {
  return this.name;
}

getName() // undefined, because "this" refers to Window
getName.bind({ name: "Bob" }) // Bob
getName.bind({ name: "Jane" }) // Jane

That was a very simple use case. The real problem occurs when we use functions as object methods and assign them to variables or use as callbacks. Then they to loose this context.

const user = {
  age: 20,
  getAge: function() {
    return this.age;
  }
}

user.getAge() // 20;

const getUserAge = user.getAge;
getUserAge() // undefined

getUserAge() returns undefined, because it lost context of the object. When we assigning object method, in this case getAge, to a variable, it no longer exists as a method, and has no reference to the user object. It becomes an independend function and is equivivalent to:

const getUserAge = function() {
  return this.age; // "this" will refer to "window" object
}

To add user object context again to the function, we need to use .bind() method at the time we assigning method to the variable:

const getUserAge = user.getAge.bind(user);

getUserAge(); // 20

Another real-world example is handling DOM element events. Again, if you will use an object method, that has this keyword, it will lose context, and this will be reassigned to the DOM element itself:

const user = {
  name: 'Bob',
  greet: function() {
    return 'Hello ' + this.name;
  }
}

const button = document.getElementById('btn');
button.addEventListener('click', user.greet); // Hello undefined
button.addEventListener('click', user.greet.bind(user)); // Hello Bob

The same lost context problem happens when using object methods as callbacks for window.setTimeout(). If the method will not be bound, the keyword this will be reassigned to the global "window" object.

Passing Arguments To The bind() Method

bind() method accepts a list of arguments after the first context argument. The use-case for that would be to create new functions with pre-specified arguments:

function add(a, b) {
  return a + b;
}

add(2, 3); // 5

const add7 = add.bind(null, 7);

add7(5); // 12
add7(10); // 17

As we don't need to provide any context for the new function, we set the first argument to null. Any other following arguments, provided to the bind() method will be replaced and set as initial arguments for the new function. The number 7 will be replaced as an argument "a", and the second argument "b" will be provided with new function call add7(5) as 5.

I have never used bind() for setting initial arguments to the new functions, but I imagined as a really useful case could be to write your own class name generator and use it for setting React class names for components:

function prefixer(s1, s2) {
  return s1 + "-" + s2;
}

const listPrefixer = prefixer.bind(null, "shopping-list");

function ShoppingListItem(props) {
 return <li className={listPrefixer("item")}>{props.item}</li>
}

After rendering component to the DOM, the list element will have a class name of "shopping-list-item".

And that's about it. A brief intro to the making bound functions. Does it make it more clear now?

Discussion

pic
Editor guide