Reputation: 994
I am trying to understand in depth how 'this' works in javascript. All I have known about this so far is,
Every function has properties and whenever the function executes, it newly defines the this property.
this refers to the object that a function is invoked to (including window object in browser).
this refers to the scope of the object(where the object is defined) instead of referring to the object itself if you use arrow syntax when defining a function because arrow function does not newly defines its own this.
The examples below are to help understanding the behaviour of this
class Example {
constructor() {
this.name = 'John';
}
method1() { //case1 : Closure
console.log(this.name);
function method2() {
console.log(this.name);
}
method2();
}
}
const a = new Example()
a.method1();
function testing(callback) {
return callback();
}
class Example2 {
constructor() {
this.name = 'John';
}
method1() { //case2: callback
console.log(this.name);
testing(function() {
console.log(this.name);
})
}
}
const b = new Example2()
b.method1();
function testing(callback) {
return callback();
}
class Example3 {
constructor() {
this.name = 'John';
}
method1() { //case3: arrow syntax callback
console.log(this.name);
testing(() => {
console.log(this.name);
})
}
}
const c = new Example3()
c.method1(); // logs 'John'
// logs 'John'
function testing(callback) {
return callback();
}
class Example4 {
constructor() {
this.name = 'John';
}
method1() { // case4: calling method as callback
console.log(this.name);
}
render() {
testing(this.method1)
}
}
const d = new Example4()
d.render()
function testing(callback) {
return callback();
}
class Example5 {
constructor() {
this.name = 'John';
this.method1 = this.method1.bind(this);
}
method1() { //case5: bind method && calling method as callback
console.log(this.name);
}
render() {
testing(this.method1)
}
}
const d = new Example5()
d.render()
I wonder how those above cases are different and what the this refers to inside each inner function and callback. Could you please explain about it? thank you :)
Upvotes: 1
Views: 47
Reputation: 5422
Since the in-depth precise explanation can be pretty big and boring, here is an exceptional article by kangax that perfectly lays it out.
And just in case, if you need a short and ultra condensed version of it here goes my short and approximate take:
When you call a function the this
is determined by the specific base value
which is usually pointing to whatever is on the left of the .
in MemberExpression
so in x.y()
this === x
, and in x.y.z()
this === x.y
.
In case of a simple CallExpression
without the .
, say just x()
,
the base value is implicitly inferred to point to undefined
, which in non-strict mode is converted to global window
and in strict mode stays the same.
This is the general mental model which should cover 99% of all the day-to-day problems with drawing the this
context out correctly.
Now on, to the actual cases:
CASE 1:
a.method1();
call has a base value a
so the this
inside of its body points to a
, so no surprises here.
method2
has implicit base value undefined.method2
, thus you have the TypeError
which explicitly states that.
CASE 2:
function testing(callback) {
return callback();
}
callback()
is called with implicit baseValue undefined
, i.e. undefined.callback()
,
and since the passed function is declared within class
testing(function() {
console.log(this.name);
})
that triggers the strict mode of code execution, that's why undefined
is not converted again to global window
, thus we have the same error as before.
CASE 3:
Arrow function
testing(() => {
console.log(this.name);
})
creates a hard binding from the this
in enclosing scope,
basically under the hood it's the same as writing:
var _this = this;
testing((function() {
console.log(_this.name);
});
That's why you get the same object resolved as this
CASE 4:
Alright, this one is interesting and needs more mechanics explanation.
So when you pass this.method
in:
render() {
testing(this.method1)
}
this.method
, but the actual underlying Function Object value, to which this reference points to, so
when it gets executed it has its this always pointing to undefined
, here look, so it's pretty much "in stone".And yes of course since this.method1
is declared in strict context again, thanks to enclosing es6 class
, undefined
remains undefined
without conversion to global window
.
CASE 5:
Same mechanics as with arrow function. Bind creates a wrapper function, which holds the cached this
value, which is not possible to override with .call
and .apply
, the same as in =>
function.
Hope this clarifies a bit it all a bit.
Upvotes: 1