Context Binding in Javascript with Closure

How to bind this to context you have no access to using closure

pancy
4 min readJul 18, 2017

Before we go on, don’t let anyone tell you JavaScript is not a programming language (if he tells you that, you should give him a hug because he sure needs one). It rightfully is. And a sneaky one.

We all know too well the untamed this, known as the One Who doesn’t Bind.

For those of you who are still on a honeymoon with JavaScript, you might want to head here and read about what you’ll soon find out about this. Or, on second thought, you might be better off turning back and returning at a later time when you need a therapy session.

Okay, you’re still here. Here is a rundown:

  • this keyword mostly depends on how, or where the enclosing function is called.
  • You cannot set this keyword to something else during execution time.
  • To make the matter worse, this behaves differently in strict mode.

Example 1: Window object

// In the browsers, the window object is globalwindow.foo = "bar";
console.log(this.foo === window.foo) // true
console.log(this === window); // true

Example 2: Function context
Most of the time, this is bound to whatever context the function is called:

// This is window/global scope
let a = (function() {
return this;
})()
console.log(a === window); // true

Example 2: Function context in strict mode
In strict mode, this is what it was set to when entering execution context. So in this case this becomes undefined.

let a = (function() {
'use strict';
return this;
})()
console.log(typeof a === 'undefined'); // true

So the only way to bind this to the surrounding context is by injecting it:

let a = (function(ctx) {
'use strict';
return ctx;
})(this);
console.log(a === window); // true

Example 3: setting this with bind
bind is a very useful method introduce in ES5 for setting this to an arbitrary context. However, we all know it’s a mystery to many JavaScript users (including myself not long ago).

let context = {foo: 'bar'};
let a = (function() {
return this;
}).bind(context)();
console.log(a === context); // true

The arrow function

In ES2015 (or ES6, what’s with the naming anyway), the arrow function was introduced. With a function declared using the arrow syntax, the this keyword behaves lexically, or binds to whatever execution context is surrounding the function it is in. For instance,

// global/window context
let a = (() => {
return this;
})();
console.log(a === window); // trueclass A {
constructor() {
this.foo = () => {
return this;
};
}
}
let a = new A();// this is bound to the class instance
console.log(a.foo() === a); // true

This kind of promoted JavaScript’s anonymous functions to be on par with lambda elites like Haskell or Clojure (shhhhhh, here give me a hug, functional hackers). I also found that it sets the enigmatic this on leash, for the the very last time. And for the first time in my JavaScript career, bind has stopped haunting me.

Then today, I ran into something resembles this:

class Foo {
constructor(name) {
this.name = name;
this.bar({
printName: () => {
console.log(this.name);
}
});
}
bar(obj) {
obj.printName();
}
}

This was tricky, at least for me. I was expecting this.name to refer to Foo’s instance’s name. However, this obviously binds to an unnamed object:

{ printName: () => {
console.log(this.name);
}
}

Because it is the immediate context between the arrow function’s and the class’s (remember arrow functions do not create a new scope for this).

I wanted to use bind, but then again I was inside a class definition, and (as far as I’m concerned) there’s no way to bind to the class instance. Seemed like the most straightforward way was to refactor:

class Foo {
constructor(name) {
this.name = name;
this.bar = this.printName;
}

printName() {
console.log(this.name);
}
}

Which looks pretty tidy and cute, but in my case is not possible or at least not straightforward.

Closure Function and Injection

Then I remembered what I once have forgotten. I didn’t have to pass an object if whatever I pass in returns one! In this case, I can pass in an anonymous arrow function that takes the context (the class instance) as an argument and refer to that instead of this.

class Foo {
constructor(name) {
this.name = name;
this.bar(((foo) => {
return {
printName: () => {
console.log(foo.name);
}
};
})(this));
}
bar(obj) {
obj.printName();
}
}

Now the bar method could remain unchanged, and I could finally smuggle the outer context in by injecting it into an arrow function!

This technique is also great for closures in older JavaScript codebase in which arrow functions are not available too. Where you normally see people using hacks like varself = this;

var self = this;
var eventHandler = function() {
console.log(self.data);
}

Because you never know in what context eventHandler will be executed, and this isn’t reliable, attaching the desired context to self and close it up in a function makes perfect sense. But if the first line was unintentionally removed and there’s already a variable named self before it, you are opening your code to a runtime bug that won’t throw an error.

By using the approach discussed, we make the function all the more functional and portable, closing the context up by injecting it into the outer function that returns a closure around the desired call:

var eventHandler = (function(self) {
return function() {
console.log(self.data);
}
})(this);

--

--

pancy
pancy

Written by pancy

I’m interested in Web3 and machine learning, and helping ambitious people. I like programming in Ocaml and Rust. I angel invest sometimes.

No responses yet