JavaScript Function Binding

In this chapter, we are going to represent the function binding: a method creating a new function that, when called, has its this keyword set to the provided value.

Now, imagine that you pass object methods as callbacks, for example, to setTimeout, and there exists a known issue: losing this.

Let’s see how to fix it.

Losing “this”

You have already got acquainted with the examples of losing this. If a method is passed separately from the object, then this is lost.

In the example below, it is shown what can happen with setTimeout:

let site = {
  siteName: "W3Docs",
  welcome() {
    console.log(`Welcome to  ${this.siteName}!`);
  }
};
setTimeout(site.welcome, 1000); // Welcome to undefined!

The output doesn’t show “W3Docs” as this.siteName. It shows it as undefined. The reason is that setTimeout received the function site.welcome, distinctly from the object. So, you may rewrite the last line, as follows:

let f = site.welcome;
setTimeout(f, 1000); // lost site context

The First Solution: a Wrapper

The most natural solution is to use a wrapper, like this:

let site = {
  siteName: "W3Docs",
  welcome() {
    console.log(`Welcome to ${this.siteName}!`);
  }
};
setTimeout(function () {
  site.welcome(); // Welcome to W3Docs!
}, 1000);

It operates as it gets the site from the external lexical environment, then calls the method regularly.

Here is a shorter option:

setTimeout(() => site.welcome(), 1000); // Welcome to W3Docs!

In this case, a slight vulnerability may appear in your code structure. And, if the setTimeout triggers site changes value, it can call the wrong object. Look at the following example, carefully:

let site = {
  siteName: "W3Docs",
  welcome() {
    console.log(`Welcome to ${this.siteName}!`);
  }
}; 
setTimeout(() => site.welcome(), 1000);
// site’s the value changes within 1 second
site = {
  welcome() {
    console.log("Another site in setTimeout!");
  }
};// Another site in setTimeout!

The Second Solution: Bind

This solution guarantees that the problem mentioned above will not occur.

Functions provide a built-in method bind, allowing to fix this.

You can use the following basic syntax:

// more complex syntax will appear a bit later
let bindingFn = fn.bind(context);

The result of func.bind(context) is a unique function-like object, callable as function. It transparently passes the call to func setting this=context .

For instance:

let site = {
  siteName: "W3Docs"
};
function func() {
  console.log(this.siteName);
}
let funcSite = func.bind(site);
funcSite(); // W3Docs

All the arguments are passed to the initial func “as is.”

For example:

let site = {
  siteName: "W3Docs"
};
function func(message) {
  console.log(message + ' to ' + this.siteName);
}
// bind this to site
let funcSite = func.bind(site); 
funcSite("Welcome"); // Welcome to W3Docs (argument "Welcome" is passed, and this=site)

Another example uses an object method, as follows:

let site = {
  siteName: "W3Docs",
  welcome() {
    console.log(`Welcome to ${this.siteName}!`);
  }
};
let welcome = site.welcome.bind(site); // (*)
// it can run without an object
welcome(); // Welcome to W3Docs!
setTimeout(welcome, 1000); // Welcome to W3Docs!
// even if the value of site changes within 1 second
// welcome site the pre-bound value
site = {
  welcome() {
    console.log("Another site in setTimeout!");
  }
};

In the (*) line, the method site.welcome is taken binding it to the site. The welcome belongs to bound functions.

In the example below, you can notice that arguments are passed “as is”, and only this is fixed by bind .

For instance:

let site = {
  siteName: "W3Docs",
  welcome(message) {
    console.log(`${message} to ${this.siteName}!`);
  }
};
let welcome = site.welcome.bind(site); 
welcome("Welcome"); // Welcome to W3Docs ("Welcome to" argument is passed to welcome)

Partial Functions

It is possible to bind not only this but also arguments. Developers use it rarely, but at times, it can be useful.

Here is the full syntax:

let bound = func.bind(context, [arg1], [arg2], ...);

It allows binding context as this and starting function arguments.

For example, here is a multiplication function sum(a, b):

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

You can use bind for creating a function twoSum:

function sum(a, b) {
  return a + b;
} 
let twoSum = sum.bind(null, 2); 
console.log(twoSum(3)); // = sum(2, 3) = 5
console.log(twoSum(4)); // = sum(2, 4) = 6
console.log(twoSum(5)); // = sum(2, 5) = 7

The sum.bind(null, 2) call initiates a new function twoSum, which passes calls to sum and fixes null as the context and 2 as the first argument. Upcoming arguments are passed “as is.”

In the example below, the value is tripled by the threeSum function:

function sum(a, b) {
  return a + b;
} 
let threeSum = sum.bind(null, 3); 
console.log(threeSum(3)); // = sum(3, 3) = 6
console.log(threeSum(4)); // = sum(3, 4) = 7
console.log(threeSum(5)); // = sum(3, 5) = 8

The benefit of making a partial function is that you can create an independent function using a readable name. You can use it not providing the first argument anytime it’s fixed with bind.




Do you find this helpful?

Related articles