Binding order for this keyword

Memosha Sinha
3 min readSep 16, 2019

--

Rules for this binding can be read

What if call site has multiple eligibility rules? So there should exist an order of precedence for the rules.
Note: — Default has the lowest priority

Which is more precedent? implicit vs explicit?

Consider the below code snippet: - 
function test(){
console.log("precedence test");
}
var obj1 = {
a:10,
test: test
};
var obj2 = {
a:20,
test: test
};
obj1.test();//10
obj2.test();//20
obj1.test.call(obj2);//20
obj2.test.call(obj1);//10

We can see here that explicit binding is having more precedence than implicit.

Which is more precedent? implicit vs new?

Consider the below code snippet: - 
function test(data){
this.a = data;
}
var obj1 = {
test: test
};
var obj2 = {};
obj1.test(2);
console.log(obj1.a)//2
obj1.test.call(obj2,3);
console.log(obj2.a)//3
var newBinding = new test(100);
console.log(obj1.a);//2
console.log(newBinding.a);//100

So, new binding is more precedent than implicit binding.

What about new binding vs explicit binding?

new & call/apply cannot be applied together, so new binding test.call(obj1) is not allowed to test new binding directly against explicit binding.
Then what do we do? Hard Binding comes to rescue. Using hard binding we can test the precedence of two rules.

Consider the below code snippet: - 
function test(data){
this.a = data;
}
var obj1 = {};
var test1 = test.bind(obj1);
test1(2);
console.log(test1.a);//2
var newBinding = new test1(3);
console.log(obj1.a);//2
console.log(newBinding.a);//3
test1 is hard bounded with obj1. but new test1(3) did not change obj1.a to 3, instead hard bounded call to test1(2) is able to be overridden with new.

More details on bind can be found on

Let’s conclude what becomes this

  • Is the function called with new ( new binding) ? If so, this is the newly constructed object.
    var test1 = new test(3);
  • Is the function called with call or apply(explicit binding), even hidden inside hard binding? If so , this is the explicitly specified object.
    var test1 = test.call(obj);
  • Is the function called within a context(implicit binding), this will be the containing object.
    var test1 = obj.test();
  • If no rule applicable, default applies. If in strict mode, pick undefined, otherwise pick global object.
    var test1 = test();

Are we done here, I would say almost completing the article. Wait can there be some scenarios where we end up binding default behaviour?

Ignored this

If you pass null or undefined as binding parameter to call, apply or bind, those values are effectively ignored and default rule is applied.

function ignored(){
console.log(this.a);
}
var a = 2;
ignored.call(null);//2

Instead of passing null, create an empty object and pass as a parameter.

function ignored(a,b){
console.log(this.a,this.b);
}
var dummyObject = Object.create(null);//spread out array as params
ignored.apply(dummyObject,[2,3]);//a:2,b:3
//currying with bind
var curryExample = ignored.bind(dummyObject,2);
curryExample(3);//a:2,b:3

Indirection

We can accidentally create “indirect references” to functions. When that function reference is invoked, default binding rule is applied.

'use strict';function indirectTest() {console.log(this.a);}var a = 2;var obj = {a: 3,indirectTest: indirectTest};var anotherObject = {a: 4};obj.indirectTest();
anotherObject.indirectTest = obj.indirectTest;
indirectTest();

Lexical this

Normal JS functions abide by 4 rules. But ES6 introduces special kind of function — arrow function which doesn’t use these rules.

Arrow function uses “fat arrow” => operator. Instead of using 4 rules, it uses “this binding” from the enclosing function or global scope.

function lexicalSample(){
return (a) => {
console.log(this.a);
};
}
var obj1 = {
a: 2
};
var obj2 = {
a: 3
};
var sample = lexicalSample.call(obj1);
sample.call(obj2);//2 not 3

--

--

Memosha Sinha

Software Engineer, Fitness lover, Traveller, wanderlust, optimistic