What I Learned at Work this Week: Bind

Mike Diaz
6 min readNov 1, 2020
Photo by Brett Sayles from Pexels

I recently picked up an assignment to dig into one of my team’s larger JavaScript files and write documentation on its structure and functions. As an aside, a couple of people had mentioned writing documentation to me as a great way to make a positive impression as a new dev and I certainly endorse it. After only a short time on this project, I understand our codebase much better and I’m looking forward to others benefitting from my work. The hard part is…I actually have to understand what I’m talking about.

The code started out simply enough — self-contained functions expanded into larger functions that all facilitated some DOM manipulation and styling. But about 2,700 lines in (big file, hence the necessary documentation), I ran into…a class declaration and constructor.

export default class ClassName {
constructor(clientInfo) {
this.email = null
...

I took a deep breath. I’ve studied object oriented programming…I’ve taught object oriented programming. I know what constructors do and how they work. Just because it’s JS and not Java doesn’t change anything. And, in truth, it really didn’t. That is until I got to the very end of the constructor:

this.show = this.show.bind(this);
this.hide = this.hide.bind(this);

The boot camp flashback was vivid — sitting in a classroom staring blankly at an instructor who was explaining JavaScript scope. I’ve always had trouble with this concept, so why not write a blog about it to help strengthen my comprehension?

This

I think that this is a confusing concept because of its very design. It is meant to be vague, malleable, applicable to any JS object, and so when we wonder what ‘this’ is referring to in a certain context, it’s because it could be referring to just about anything. To refresh our memory, let’s look at a version of a classic JavaScript example:

let takeAction = function() {
console.log(this.action)
}
let tree1 = {
action: 'photosynthesis',
takeAction = takeAction
}
let tree2 = {
name: 'Leafy',
takeAction = takeAction
}
tree1.takeAction() // => 'photosynthesis'
tree2.takeAction() // => undefined

This behavior is unsurprising, after all tree2 doesn’t have an action attribute. But maybe we want to give tree2 the takeAction method from tree1, which returns ‘photosynthesis’. Could we do this instead?

tree2.takeAction = tree1.takeAction

We might intuit that tree1.takeAction will bring ‘this’ with it and therefore assign treen2 a function that references tree1’s action. Fans of JavaScript gotchas will know that this is not the case, and instead tree1.takeAction will only assign the base function itself. Even after this line, if we run tree2.takeAction(), we’ll get undefined. All that’s happening is that our compiler is looking to tree1 to find the function, but it doesn’t take ‘this’ with it.

Bind

JavaScript has a few ways of addressing the ‘this’ issue, but bind is what tripped me up at work, so we’ll investigate that one. Bind allows us to declare a point of reference for our JavaScript functions, essentially letting us decide what ‘this’ is. Let’s look at how we could use bind to solve our problem from earlier

let takeAction = function() {
console.log(this.action)
}
let tree1 = {
action: 'photosynthesis',
takeAction = takeAction
}
let tree2 = {
name: 'Leafy',
takeAction = takeAction.bind(tree1)
}
tree1.takeAction() // => 'photosynthesis'
tree2.takeAction() // => 'photosynthesis'

By calling .bind on the takeAction function, we gain the opportunity to decide on its scope. Bind takes an argument of the object that we want to make our ‘this.’ It’s plain and simple, which actually made my situation more confusing. In the examples I we see here, we’re binding a function to an object, but in my codebase, the function was defined as a property of a class and then, apparently, re-bound to that class:

this.show = this.show.bind(this)

What could possibly be the use of this line?

Classes

One thing I noticed about the examples I was seeing in Google searches is that they dealt with simple objects rather than classes. So, just to check my work, I built a tree class:

class Tree {
constructor() {
this.action = 'photosynthesis'
this.takeAction = this.takeAction.bind(this)
}
this.takeAction = function() {
console.log(this.action)
}
}

In JavaScript, classes are very similar to objects, though they are defined and invoked using slightly different syntax. Our constructor indicates what properties a class is assigned on instantiation, which here we can see is an action and a method to log that action (methods are functions that are associated with a class). The method is assigned within the constructor, but defined outside, which followed the same pattern as the class in my work codebase: a ‘show’ method was defined outside the constructor and bound within the constructor.

I created a tree instance and ran takeAction, getting the result I expected:

const newTree = new Tree()
newTree.takeAction() // => 'photosynthesis'

So is the binding required to give the method the proper context? I tried commenting out the bind…but got the same result. My next step was to try and make my example more similar to the workplace execution. Tracing the call stack, I learned that our class is initialized by one function in another file and then show is called on that instance in a separate function, which we can call runShow. runShow is called all by itself, though of course it’s wrapped in an if/else inside of a try/catch (I’m exhausted). I wonder if emulating this in my version will change the behavior:

class Tree {
constructor() {
this.action = 'photosynthesis'
// this.takeAction = this.takeAction.bind(this)
}
takeAction() {
console.log(this.action)
}
}const initTree = () => {
newTree = new Tree()
}
const runTakeAction = function(){
newTree.takeAction()
}
initTree()runTakeAction() // => 'photosynthesis'

Oh you’ve got to be kidding me — it still works! What is the point of that bind? As I looked over the code to make sure I wasn’t missing something, I noticed that the show method was actually passed on in another way:

window.trigger = className.show

So I tried it out:

...
const initTree = () => {
newTree = new Tree()
window.trigger = newTree.takeAction
}
const runTakeAction = function(){
newTree.takeAction()
}
initTree()runTakeAction() // => 'photosynthesis'
window.trigger() // => undefined

Eureka! When we assigned takeAction to another class/object, in this case the window object, it returned undefined! Even though my workplace example was thousands of lines long and spanned multiple files, functions, and methods, it actually closely resembles the simple implementation of bind. Even if we pass along newTree.takeAction, we’re still only sending along the basic function itself. So ‘this’ changes to the window object if the method is called by window. By using bind, we fundamentally change the definition to say that ‘this’ will always be the instance of the class it’s associated with.

Trust Yourself

This journey was a super useful review of the bind syntax, but also one of self-discovery. I went down several deep rabbit holes trying to figure out what I was missing about bind or what obscure patterns my coworkers had used that necessitated this bind. I thought that I was missing a fundamental understanding of the method, but in truth I was just missing an instance of implementation. These are important lessons to learn as a programmer: when you’re struggling, take a break, sleep on it, and try approaching the problem from a different angle. It can make a big difference!

Sources

--

--