DEV Community

Cover image for Let's talk about Hard Binding in JavaScript
Carlos Fuentes
Carlos Fuentes

Posted on • Edited on

Let's talk about Hard Binding in JavaScript

After a busy week, I'm here and I want to talk with you (the reader) some confusing (at less for me) and interesting concept in JavaScript.
Currently, I'm involved in different groups on facebook where we have as a principal topic talk about programming. Advice, trending, news and much more about that; obviously, all in Spanish (I'm from Mexico if you don't know).

One of these groups is exclusively about Node.js and JavaScript (if you're Spanish-speaking, I invite you to join. Here's the link). After reading posts for a while, I found this code:

function foo() {
console.log(this.bar);
}
var obj = { bar: "bar" };
var obj2 = { bar: "bar2" };
var orig = foo;
foo = function(){ orig.call(obj); };
foo();
foo.call(obj2);
Enter fullscreen mode Exit fullscreen mode

Take your time to think about what this piece of code is going to print and then continue with the article. I wait for you, do not worry.

Thinking about it

Whether you get it or not, this code snippet prints bar twice in a row.

Here the link to see it and yeah, is his code.

Well, we get this result because of one thing: Hard Binding

Ok?

And what is Hard Binding?

To have a good understanding of Hard Binding is important to reach some confusing things before getting a hard binding. One of these is this.

This

Is a mechanism that is most commonly used in the OOP programming paradigm. In that cases, we talk about the context of the object what are creating. For example, the uses of his functions and global variables. In JS, the history is some confusing but nothing of the other world.

In JS, this is a contextually based mechanism which, in turn, is based on the function invocation and not in the where the function was declared. When a function is invoked a call-stack is created and with this call-stack, a kind of record too. In this record is saved the information of the function with data of how and where has invoked, what parameters are passed and more. With all of that information, this comes as one of those properties. At the end, the record is alive as the duration of the function execution.

Binding

In JS, the context in where we invoke a function has a lot of importance.

Think about this function:

function something(){
  var a = 2;
  console.log(this.a);
}

var a = 5;

something();
Enter fullscreen mode Exit fullscreen mode

What is going to print?

I let you try that code and explain what exactly happens.

When we call this, we are invoking the context of the global object. In other words, we are taking properties (in this case, the variable a) declared as global outside of the function. Doesn't matter if I create a variable called as the same of the outside variable, I taking properties from the global object, not from the function context.

Implicit Binding

Now, what about this code. What is going to print?

function something(){
  console.log(this.a);
}

var a = 5;

var obj = {
  a: 10,
  something: something
}

obj.something()
Enter fullscreen mode Exit fullscreen mode

When we have this, we are applying something called Implicit Binding . . . and with what I can eat that?

Well, as you see in this case, we are assigning the context as an object to the function. In a more simple way, we are creating the context of the function as an object. So, instead of taking the global context, take the context of the object we made.

With this comes a problem, what if we do this?

function something(){
  console.log(this.a);
}

var obj = {
  a: 10,
  something: something
}

var cake = obj.something

var a = 5;

cake();

Enter fullscreen mode Exit fullscreen mode

Well, we lost the object context and we had the global context again, but why?
Remember when I told you that the invocation matters? Well, this is the reason why.

We lost the context of the object because we assign the function to a variable, obtaining a reference to that function, changing the context. When we do that, a implicit lost occurs and we obtain the global context (for this case).

The same happens if we do this:

function something(){
  console.log(this.a);
}

function anotherFunction(fn){
  fn();
}

var obj = {
  a: 10,
  something: something
}

var a = 'Five';

anotherFunction(obj.something);
Enter fullscreen mode Exit fullscreen mode

Remember, invocation matters

Explicit Binding

As everything, every problem has a solution (or we can make one, but in this case has one as default).

If we want to force a function to get a particular context of an object without an implicit reference, we can use functions like call(...) (more info) or apply(...) (more info).

Calling call() and passing the object that you want to apply as an argument, forces the function to use the context of the object. For example, like this:

function something(){
  console.log(this.a);
}

var obj = {
  a: 10,
}

var a = 'Five';

something.call(obj);
Enter fullscreen mode Exit fullscreen mode

As above, we can use apply(this, []) instead. Passing an object and an array as arguments.

Hard Binding

At this point, I hope everything is fine. You understood everything I wrote, I suppose. Now with this, maybe you want to know: and a hard binding, what about that?

Well, let me tell you something. With this solution we do not solve all the binding problems, you know?

We back to the first code that I found:

function foo() {
console.log(this.bar);
}
var obj = { 
  bar: "bar" 
}
var obj2 = { 
  bar: "bar2" 
}
var orig = foo;
foo = function(){ 
  orig.call(obj);
}
foo();
foo.call(obj2);
Enter fullscreen mode Exit fullscreen mode

Why do that?

Put attention just in this line: foo = function() { orig.call(obj) }
Ok, you see it? That's is hard binding.

That function makes that foo always do the same action every time that is called, no matter what is the invocation context. Make a function that calls internally and manually an explicit binding and force to do the same instruction no matter where and how you invoke that function

As we talk above, Every problem has a solution (or we can make one):

function foo(something) {
  console.log(this.bar + something);
  return this.bar + something
}

var obj = { 
  bar: 2
}

var obj2 = {
  bar: 4
}

function bind(fn, obj){
  return function(){
    return fn.apply(obj, arguments)
  }
}

var result = bind(foo, obj);

var result_2 = bind(foo, obj2);

result(2);

result_2(2);
Enter fullscreen mode Exit fullscreen mode

Or, we can use the one that ES5 gives us

function foo(something) {
  console.log(this.bar + something);
  return this.bar + something
}

var obj = { 
  bar: 2
}

var result = foo.bind(obj);

result(2);
Enter fullscreen mode Exit fullscreen mode

This utility was introduced by ES5 in Function.prototype.bind (more info), it gave us the hard binding in an easier way.

One more thing

Feel free to make comments and corrections to this article. I do this article just for explain and understand more clearly how Binding works and what is Hard Binding. If you want to know more about Binding or JavaScript in general, you can read You-Dont-Know-JS and understand more clearly this and more things about JS.

See ya.

Top comments (0)