The this
keyword in JavaScript is used as a reference to the scope of the current execution context. What this means is that it represents an environment that contains the code that is currently running and everything that aids in it’s execution. This by default is the global object.(window in browser).
var a = 20
console.log(this) //Window
console.log(this.a) //20 (all variables in th global scope are a property of the global object
However, all functions have their own individual execution context created for them when they are called and the value of this in each function is determined by how the function is called. Note: How it is called, not declared
In non strict mode. The this keyword always represents an object.
This
in Functions
For a typical function, the value of this
is the object that it is accessed on. i.e the object it is called on as a method. This is known as implicit binding. We use the word ‘binding’ to demonstrate how the JavaScript engine attaches ‘this
’ to an object.
function foo(){
console.log(this.a)
}
const obj = {
a:2,
print:foo
}
obj.print() //2
If the function is called without being accessed on anything, this
is set to the global this
. This is known as default binding and only occurs in non strict mode. In Strict mode it returns undefined
var a = 5
function foo(){
console.log(this.a)
}
const obj = {
a:2,
foo:foo
}
obj.foo() //2
foo() //5 (this resolves to global this)
New Binding
If the function is called as a constructor with the new
keyword. this
defined inside the function references the newly constructed object. This is the same behaviour for classes as classes behave like constructor functions.
function Foo(a, b){
this.a = a
this.b = b
this.sum = function(){
return this.a + this.b
}
}
const mySum = new Foo(5,7)
console.log(mySum.a)//5
console.log(mySum.b)//7
console.log(mySum.sum())//12
Explicit binding
You can also explicitly set the value of this
in a function using some special built in methods that exists on all functions. This makes sure no matter how the function is called, this remains the same.
They are
Function.call()
Function.apply()
Function.bind()
Function.apply
and Function.call
work very similar in the context of our discussion. They call the function with a given value for this
passed as an argument. The function is then executed with the first argument used as this
function sum(){
console.log(this.a + this.b)
}
const obj1 = {
a: 8,
b: 5
}
const obj2 = {
a: 2,
b: 6
}
sum.call(obj1) //13
sum.call(obj2) //8
Function.bind
however takes an argument and returns a copy of the same function that when called has it’s this value bound to the argument.
function sum(){
console.log(this.a + this.b)
}
const obj1 = {
a: 8,
b: 5
}
const obj2 = {
a: 2,
b: 6
}
let sum1 = sum.bind(obj1)
sum1() //13
let sum2 = sum.bind(obj2)
sum2() //8
Conclusion
As we can see there are different ways our JavaScript engine binds this
inside a function when said function is being executed. But what happens when there are multiple bindings in a function, how do we determine what this
is?
Determining the this
binding for an executing function requires finding the direct call-site of that function. Once examined, four rules can be applied to the call-site, in this order of precedence:
Called with
new
? Use the newly constructed object.Called with
call
orapply
? Use the specified object.Called with a context object owning the call? Use that context object.
Default: undefined in Strict Mode, global object otherwise.
this
in Arrow functions
With arrow functions there are no binding of this
.
In regular functions the this keyword represented the object that called the function, which could be the window, the document, a button or whatever. With arrow functions the this keyword always represents the object that defined the arrow function.
const a = 10
const foo = ()=>{
console.log(this)
}
let obj1 = {
a:5,
foo:foo
}
obj1.foo() //[Window Object] it returns the global window object because foo is defined in the global scope.
Arrow functions are used preferably as callbacks for events, etc to prevent this
binding being overridden during code execution.