The most dumbed down definition I have is: 'this' is the object that calls a function or the object where an arrow function is called
A more technical study is dynamic scope
vs lexical scope
;
Who is 'this' in the for each function call?
// 1. this in a function
function mySingleFunc() { console.log(this) } // this is the Global object
// 2. this in a constructor function
const myObject = new function(){ this } // this is the new object 'myObject'
// 3. this in an object method function
myObject.myFunc = function() { console.log(this) } // this is myObject
myObject.myFunc();
// 4. this in an inner function of an object method function
myObject.myFunc = function() {
const innerFunction = function() { console.log(this) } // this is the Global object
innerFunction();
}
myObject.myFunc();
// 5. this in an async function callback in an object method function
myObject.myFunc = function() {
setTimeout(function() { console.log(this) }, 0) // this is the Global object
}
myObject.myFunc();
// 6. this in an object method arrow function
myObject.myArrowFunc = () => { console.log(this) } // this is the Global object
myObject.myArrowFunc();
// 7. this in a inner arrow function in an object method function
myObject.myFunc = function() {
const innerArrowFunction = () => { console.log(this) } // this is myObject
innerArrowFunction();
}
myObject.myFunc();
// 8. this in an async arrow function callback in an object method function
myObject.myArrowFunc =() => {
setTimeout(() => { console.log(this) }, 0) // this is the Global object
}
myObject.myArrowFunc();
// 9. this in a inner arrow function in an object method arrow function
myObject.myArrowFunc =() => {
const innerArrowFunction = () => { console.log(this) } // this is the Global object
innerArrowFunction();
}
myObject.myArrowFunc();
Checkout this plunk for the live examples
Let's dig deeper
You should know by now that when you call myObj.myFunc()
, the this
in myFunc()
is myObj. myObj is the scope
of myFunc;
This is an important concept to keep in mind in order to know who does what.
Whenever you declare a simple function myFunc() {}
at the root level of your code, that function is automatically bound to the Global scope. Which means that whenever you call that function by name, ex: myFunc()
what happens behind the scenes is actually window.myFunc()
in the browser or global.myFunc()
in nodeJs.
myFunc() === window.myFunc()
But when you assign a function to a property of an object (myObj.myFunc = function() {}), that function becomes a method of that object and also is bound to that object. That's why, the this
in that function is myObj, and myObj is the scope
of myFunc
.
Keep in mind, that if you declare/call a function (myInnerFunc
) inside a method
of an object, it's like you're calling myObj.myFunc()
and then inside myFunc, you are actually doing window.myInnerFunc()
, because myInnerFunc
is not bound to myObj
but just declared inside a method of that object. And that's why, the this
inside the inner function is actually the global
object. Same applies for callbacks of setTimeout
, setInterval
, or other async
stuff you might do inside your function.
However, things change for arrow functions () => {}
, as these functions don't have their own this
binding and they shouldn't even be used as methods.
That's why, if you declare a method as an arrow function, altough the call looks like myObj.myArrowFunc()
and you might think this
is myObj, myArrowFunc will actually take the this from the scope in which it is called, and in this case, this
will be the global object. They borrow their this
from the wrapping scope in which the function is called. Same happens if there is an inner arrow function inside the method arrow function: it will be called in the scope of myArrowFunc which is the global one.
But, if you have a myFunc: function(){}
method of an myObj
object and inside you have myArrowFunc
arrow function, the this
of the arrow function will be the actual myObj
, because myFunc will have a scope that is myObj, and the arrow function will be called in the myObj
scope. Same happens if you have an arrow function as a callback inside an async function. The scope in which it will be called, it will still be myObj
.
That's about it. Play with the examples and see for yourselves.