this keyword is an integral part of the Javascript language. Yet sometimes it gets confusing to detect its value under different scenarios. In this article, I’ll explain ways by which you can find out what is the value of this keyword by analysing the call-site.
What is this?
this represents the binding that is made when a function is called and the site, where the function call is made, is known as call-site.
How to find a call-site?
To understand this binding, we must first understand call-site. To find out call-site you should inspect where the function is called from. But finding out the call site is not always straightforward, as some coding patterns conceal it.
Demonstration of call-stack and call-site.
As defined by MDN. A call stack is a mechanism for an interpreter (like the JavaScript interpreter in a web browser) to keep track of its place in a script that calls multiple functions — what function is currently being run and what functions are called from within that function.
Let's understand this with the help of some examples.
function baz() {
//call-stack is: `baz`
//so, our call-site is in the global scope
console.log("baz")
bar() // ← call-site for `bar`
}
function bar() {
// call-stack is `baz` --> `bar`
// so, our call-site is `baz`
console.log("bar")
foo() // ←- call-site for `foo`
}
function foo() {
// call-stack is `baz` -> `bar` -> `foo`
// so, our call-site is in `bar`
console.log("foo")
}
baz() // ← call-site for `baz`
A view of debugger tool of the chrome browser showing the call stack.
To view the call-stack in your browser just put the debugger on the first line of each function. When you run the page, the debugger will stop at the point where you have a debugger and show the functions called to reach this line.
Determining the value of this
Now that you have understood call-site we will look into different types of binding which happen by calling the function in different ways.
Default Binding
Let's take an example
function foo() {
console.log(this.a)
}
var a = 2
// call-site
foo() //2
In the above example after examining the call-site we can notice that function call tofoo() is a standalone function call also known as a standalone function invocation.
The type of binding which happens during standalone function invocation comes under the category of default binding.
In the function foo()
this.a
resolves to global variable a
because this here belongs to the global scope as the default binding rules are applied here and our variable var a = 2
is also declared in the global scope.
Whenever a variable is declared in the global scope the global-object contains the property with the same name.
Note: If you change var a
to let a
in our example. You will get this.a
as undefined. Because according to the JavaScript specs let and const doesn’t get defined on windows object.
Implicit Binding
The implicit binding rules apply when the call-site has a context object.
function foo() {
console.log( this.a )
}
Var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
The implicit binding rule states that when a function call is preceded by the object reference it’s that object that should be used for the function’s this binding. Which is obj
in our case.
It's important to note that you may lose implicit binding and fallback to default binding in case of the implicit lost.
Let's understand this by an example.
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo,
}
var bar = obj.foo // function alias
var a = "global variable"
bar() // global variable
You may be thinking that in the above example implicit binding rule should apply because bar
refers to obj.foo
. But as stated earlier while determining this, what matters is the call-site and the call-site here is bar()
which is a stand-alone function call. That’s why the default binding rule applies here.
Explicit binding
We can use explicit binding to force a function call to have to use a particular object for this binding, without putting a property function reference on the object as we did in the case of implicit binding. All function in JavaScript have call(), apply() and bind() methods available to them. We can use these functions to achieve explicit binding.
e.g
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
}
foo.call(obj)
In call-site foo.call(obj)
. The call
function calls foo
with this given as the first parameter to call() which is obj
in our example.
So, in the function foo
this will point to obj
.
Invoking foo
with explicit binding by foo.call(...) allows us to force its this be obj
.
new binding
When a function is invoked with new keyword in front of it, a new type of binding occurs, known as new binding.
These things happen when we call a function with new
- A brand new object is created.
- The newly constructed object is linked to the Prototype chain.
- The newly created object is set as the this binding for that function call.
- The new invoked function call will return a newly constructed object. e.g
function foo(a) {
this.a = a
}
var bar = new foo(2)
console.log(bar.a) //2
In the above example, we have called the foo() with new operator in front of it. This call will create and return a new object bar
which is the this for the call to foo()
.
Summary
To summarise these are the points you should keep in mind while detecting value of this keyword
- First, it checks whether the function is called with the
new
keyword. - Second, it checks for explicit binding by checking if the function is called with call(), apply() or bind().
- Third, it checks if the function is called via context object (implicit binding).
- Default global object (undefined in case of strict mode).