es5
In non-strict environment, the ‘this’ keyword is bound to the context that is calling it
Context refers to the object to which a function belongs.
1 2 3 4 5 6 7 8 9 |
// es 5 function Greetings() { // When it is inside of an object’s method, the context is the instance because it is the instance that is calling the method. this.print = function() { console.log('print', this); // instance Greetings {...} because the instance is calling print } } |
Yet when ‘this’ is inside of a function:
- either stand-alone function
- or within another method
it will always refer to the window/global object
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// es 5 function Greetings() { this.print = function() { // method console.log('print', this); // instance Greetings {...} because the instance is calling print function inner() { // within another method // console.log('inner', this) // global } inner(); } } |
stand-alone
1 2 3 4 5 6 7 8 9 |
function Greetings() { console.log('standalone -----> ', this); // global function haha() { console.log('standalone haha ------> ', this); // global } haha(); } Greetings(); |
…within method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var obj = { hoho: function() { // method //console.log(this); // obj function hehe() { // within method //console.log(this); // global function haha() { // within method console.log(this); // global } haha(); } hehe(); } } obj.hoho(); |
Use strict
However, if you were to use strict mode, ‘this’ inside functions that are standalone, or within a method, would reference undefined
1 2 3 4 5 6 7 |
'use strict' function Greetings() { console.log('standalone -----> ', this); // undefined } Greetings(); |
standalone functions within functions.
functions within methods
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
'use strict' function Greetings() { function haha() { console.log('standalone haha ------> ', this); // due to strict: undefined } haha(); return { print : function() { // method console.log('print -->', this); // calling instance function hehe () { console.log('hehe', this); // undefined } hehe(); } } } let g = new Greetings(); // create instance g.print(); |
es6
With arrow functions, ‘this’ is lexically bound. It means that it uses ‘this’ from the code that contains the arrow function. In other words, it always uses ‘this’ from the parent scope.
class Greetings
In the class Greetings, for the constructor function’s this, we see that the parent scope’s ‘this’ is class Greetings. Thus, the ‘this’ references the instance Greetings.
In the method ‘print’, the parent scope is the constructor function. The constructor function’s ‘this’ is the instance (Greetings).
In the private method ‘haha’, the parent scope is also the constructor function. The constructor function’s ‘this’ is the instance (Greetings).
in setTimeout, the callback function’s ‘this’ parent scope is the private function haha. As mentioned above, private function haha’s context is the instance (Greetings).
In the next example, we are executing a callback. We are using an es5 function. es5 function ‘this’ references the object that is calling it. In our case, the setTimeout’s context is executing this callback function. Thus, ‘this’ references a Timeout instance.
Finally, we have the method ‘print2’. Since its an arrow function, its parent scope is class Greetings. Hence its ‘this’ references the instance (Greetings).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
class Greetings { constructor() { console.log('Greetings', `constructor`); console.log('Greetings constructor', this); // instance var name = "default"; // public this.print = () => { console.log('Greetings:print -- ', this); // instance haha(); } // private const haha = () => { console.log('Greetings', 'private function haha') console.log(this) // instance // notice use of es6 arrow function setTimeout(() => { console.log('Greetings:haha', 'using es6 arrow function') console.log(this); // instance console.log(`hello ${name}`); // 'hello default' }, 2000); } // callback // notice use of es5 function setTimeout(function() { console.log('Greetings', 'using es5 function') console.log(this); // Timeout console.log(`hello ${name}`); // 'hello default' }, 2000); } // public print2 = () => { console.log('Greetings:print2 -- ', this); // instance } } let f = new Greetings(); f.print2(); f.print(); |
Literal Object bracket is NOT lexical scope
Our arrow function uses ‘this’ from the lexical scope that contains the arrow function. In the case of arrow function x, bar is the literal object that contains our arrow function.
It is NOT a lexical scope
Hence, bar’s this comes from the lexical scope that is the global scope. Global scope’s this is {}. Hence that is what the ‘this’ is inside of arrow function x.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
console.log('global', this); // {} let bar = () => { console.log("--- obj calling function bar --- ") console.log(this) // {} var x = () => { console.log(" --- in arrow function --- ") console.log(this) // {} }; return x; } let x = bar(); x(); |
In this example, we have a literal object. Then we have es5 function bar. The ‘this’ is bound to the calling context, which is literal obj. That is the ‘this’ inside function bar is ‘obj’.
Then, inside function bar, we have es6 arrow function. Arrow function says ‘this’ is bound to its lexical parent. Hence, since its lexical parent is bar’s this, it references literal obj. Thus that’s why in arrow function x, the ‘this’ also references ‘obj’.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var obj = { // function reference 'bar' bar: function() { console.log("--- obj calling function bar --- ") console.log(this) // references the outer literal object 'obj' var x = () => { console.log(" --- in arrow function --- ") console.log(this) // references the outer literal object 'obj' }; // returns a reference to a function return x; } }; // object literal let x = obj.bar(); x(); |
output:
— obj calling function bar —
{ bar: [Function: bar] }
— in arrow function —
{ bar: [Function: bar] }
ref – https://stackoverflow.com/questions/31095710/methods-in-es6-objects-using-arrow-functions
Our arrow function uses ‘this’ from the code that contains the arrow function The containing code is the literal object.
Since the literal object itself has no ‘this’, we cannot say it will log profile. Instead, the ‘this’ of literal object ‘profile’ is the global scope. And ‘this’ at global scope is {}
Lots of times, I would mistakenly believe that it should be literal object ‘profile’. That would be true in es5 function because it binds ‘this’ to the calling object as shown in the above example.
However, since we’re dealing with es6 arrow function, we bind ‘this’ to the parent scope’s this. NOT the parent object.
Hence, we cannot bind it to the literal object profile. We can only bind it to profile’s ‘this’.
Literal objects do not have ‘this’ context. So profile’s this defaults to global {} because literal object profile is at the global scope.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// es6 // One of the biggest points of the new function syntax was that it treats this differently. // It's defined by the lexical environment in which the function was created, // which means the 'this' value where you create the profile variable will be the 'this' value of the function. // In other words, it won't be referencing the profile object. const profile = { // 'this' context of profile is outer 'this' name: 'Alex', getName: () => { console.log(this); // {}, it is actually 'this' at the global scope return this.name; } }; |
click handlers
1 2 3 4 5 6 7 8 |
<button onclick = "test(event, result => { console.log(this); // the DOM element button })" > test </button> |
means that when the element is clicked, the function ‘test’ is fired. But this function test is bound as an event handler, so ‘this’ is set to the reference to the button DOM element.
In test function’s definition, the this is the global obj, as it is when any function that’s not an event handler or an object method is.
1 2 3 4 5 6 |
<script> function test(evt, cb) { console.log(this); // global (window) cb('haha'); } </script> |