1. Object Literal
Whenever a function is invoked as a property of an Object it always has the 'this' keyword referring to the Object itself. An example will further clear things up:
The car variable is set to an object literal it has 'make', 'model' and 'getModelName' properties. When the function 'getModelName' is executed it outputs the value of 'this' to be the object itself and so it is able to access the model using this.model syntax. In this case context is the owner Object (Object literal).
2. Global Variable Invocation
The different function in the example have certain depth and it is evident that no matter what the depth is 'this' will always be the 'window' Object.
For testFunc1 it is visible that the value of 'this' is the 'window' Object.
The testFunc2 returns another function after first invocation which is assigned to variable 'tf2'. But after executing 'tf2' it displays the value of 'this' to still be the 'window' Object.
Similarly testFunc3 returns another function down to 2 more levels. But again the value of 'this' is the window Object which proves that no matter how many levels deep function calls go 'this' always refers to the 'window' Object if the parent function is declared globally and all subsequent function calls are invoked without 'new' prefix.
3. Constructor Invocation
When a function is invoked using the 'new' prefix 'this' is always the function itself. This is comparatively simpler concept to grasp. But it will be evident that it can get tricky. Lets continue with 'testFunc3' from previous example and modify it a bit:
In this example 'this' keyword is logged for every level. So lets see what each level displays.
The 'level1' function invocation with 'new' prefix shows 'this' to be 'testFunc3' which is the function itself as expected. Now lets see the next level.
Even in 'levle2' function invocation with 'new' yields 'this' to be 'testFunc33' which is the function itself. Moving on to next level but invocation without 'new' prefix.
It seems now the function has 'this' as 'window' Object and not the function itself. This is because if a context-less(not a property of parent object) function is invoked without 'new' prefix 'this' will be bound to the 'window' Object not matter how deeply nested the function is. If 'new' prefix is used while invoking 'tf333' then 'this' becomes function itself as expected.
4. ES6 Arrow function
Now 'testFunc333' is an arrow function. Lets see what it ouputs for this.
It is evident that arrow functions preserve 'this' from the parent function. So now 'level3' and 'level2' have same 'this'. This is way 'testFunc333' will no longer be bound to 'window' Object.
5. call, apply and bind
Using call, apply and bind the value for 'this' can be explicitly set. This is convenient when 'this' can get hard to manage in large applications.
The '.bind(this, arg1, arg2)' function when used on existing function will return a new function with 'this' changed and with arguments passed to the function.
The 'printPrice' function prints price of a car. The 'car' Object has information about a certain car. When 'printPrice' is invoked with price as 100000 the output has undefined in it. This is because 'this' is bound to 'window' Object. But 'make' and 'model' are not defined in 'window' Object this is why it returns undefined for those values. But when using 'bind' method the first argument 'car' sets the value of 'this' to be 'car' Object and the second argument passes price. This creates a new 'printPrice' function with 'this' and 'price' already in place.
The call and apply function are similar to each other as they directly invoke the function instead of return a new one. The only difference is that 'call' has comma seperated arguments after the first argument and 'apply' has an array argument for the second argument. Both can be used to explicitly set value for 'this' in a function and also provide arguments for the function.
The 'scaleNumbers' function will scale the any passed number by 'pi' using 'this.pi'. The 'constants' Object literal contains value of 'pi'. But the value of 'this' in 'scaleNumbers' function is 'window' Object so 'this.pi' will be undefined. To fix this 'call' and 'apply' can be used to set 'this' and invoke the function with numbers. The 'call' method takes first argument to be value of 'this' which is the 'constants' Object literal and second argument is comma separated list of numbers. The 'apply' method takes first argument to value of 'this' as well but the second argument is an array containing numbers.