Javascript Function Binding

gduple

Grant

Posted on June 29, 2020

Javascript Function Binding

Function binding is important because it defines the object on which a function will be called. Consider the example below:

   const whereYat = "Outside";

   const mainObj = {
      whereYat: "Inside",
      find: function() {
         return whereYat;
      }
   };

   const otherObj = {
      whereYat: "Ain’t dere no more."
   };

   mainObj.find(); // “Outside”
Enter fullscreen mode Exit fullscreen mode

There are three locations where something is named whereYat: a variable (const) declared on the global scope, a property of the object named mainObj, and a property of the object named otherObj. The function inside mainObj, when called, will return one of these whereYat values. As written, it will return Outside, since the function call is made on the global scope, where the variable whereYat lives.

Intuitively, the purpose of this code snippet would appear to be different than its current result, but how does one take control of what value function find returns? The first thing our code needs would be keyword this:

  const whereYat = "Outside";

   const mainObj = {
      whereYat: "Inside",
      find: function() {
         return this.whereYat;
      }
   };

   const otherObj = {
      whereYat: "Ain’t dere no more."
   };

   mainObj.find(); // “Inside”
Enter fullscreen mode Exit fullscreen mode

Keyword this can lead to much confusion, but its use is necessary to prescribe exactly which object a function will operate on. The updated example is called “method invocation”, where this refers to the object on which the function (method) is called. A helpful tool is to look at what’s to the left of the dot at call time. This code returns the value of whereYat from inside mainObj. But what if we wanted the function to return the value of whereYat inside otherObj? Passing otherObj as an argument doesn't work because this is still bound to the object on which the method is called (the object to the left of the dot). Therefore, mainObj.find(otherObj) still returns Inside.

The keywords .call(), .apply(), and .bind() are helpful here, as they allow the function call to be applied to a passed-in object:

  const whereYat = "Outside";

   const mainObj = {
      whereYat: "Inside",
      find: function() {
         return this.whereYat;
      }
   };

   const otherObj = {
      whereYat: "Ain’t dere no more."
   };

   mainObj.find.call(otherObj); // "Ain’t dere no more."
Enter fullscreen mode Exit fullscreen mode

In the updated function call, the .call() method links keyword this to the argument object, otherObj. Using .apply() would give the same result here. But what happens if the function call is made with .bind()?

mainObj.find.bind(otherObj); // returns the actual find [function]
Enter fullscreen mode Exit fullscreen mode

The string at otherObj's whereYat key isn't returned because .bind() doesn't actually call the function like .call() (and .apply()). To return the value we're looking for, we would have to call the .bind() method immediately:

mainObj.find.bind(otherObj)(); // "Ain’t dere no more."
Enter fullscreen mode Exit fullscreen mode

This difference may make .bind() appear to be less useful. .bind(), howver, has a special capability .call() and .apply() don't have, which is to permanently "bind" the function to the argument object.

  const whereYat = "Outside";

   const mainObj = {
      whereYat: "Inside",
      find: function() {
         return this.whereYat;
      }
   };

   const otherObj = {
      whereYat: "Ain’t dere no more."
   };

   const bindToOther = mainObj.find.bind(otherObj); 
   bindToOther(); // "Ain’t dere no more."
Enter fullscreen mode Exit fullscreen mode

By setting the .bind() method call to a variable (bindToOther), we've saved the connection between otherObj and the mainObj.find method. We can call this configuration anytime we want using the bindToOther variable name. For example, let's say we use .call() again to call the function on mainObj, returning Inside:

   mainObj.find.call(mainObj); // “Inside”
Enter fullscreen mode Exit fullscreen mode

We can go right back to the bindToOther() variable to once again return the result of calling the find method on otherObj:

   bindToOther(); // "Ain’t dere no more."
Enter fullscreen mode Exit fullscreen mode

This method of remembering specific function calls is useful not only in reducing code repetition, but also in reducing bugs that result from unintended function binding.

💖 💪 🙅 🚩
gduple
Grant

Posted on June 29, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related