I'm having real trouble getting my head around some fundamental concepts in JavaScript so am hoping someone can give me a good explanation of what is happening under the hood in these examples.
CASE 1:
In this case I use the 'this' keyword in a constructor function:
function Counter() {
this.c = 0;
this.incC = function() {
return this.c++;
};
}
Obviously now I can instantiate the function and call the incC function to increment c and a record of c will be held in the created instance as the 'this' refers to the actual object being created. Here from my (limited) experience programming in C I imagine that when I do:
var counter = new Counter();
-the code is effectively allocating space for the object and then passing a pointer to that allocated memory into the constructor and that pointer is the 'this'.
But I have to wonder if I'm wrong because in case 2 things are a little different.
CASE 2:
In this function I can create a persistent variable using the 'this' keyword. However, I am not going to instantiate it, so it cannot be pointing to an object and it doesn't seem to be an attribute of counter (obviously the function itself is an object) as counter.c returns undefined.
function counter() {
this.c = this.c || 1;
return this.c++;
}
console.log(counter.c); // undefined
So what is happening under the hood in this example? Why and how is c not being lost when we exit the function and what is the 'this' pointing at in this example?
CASE 3:
Finally just to make things more interesting.
I came across this in a very old code base (I know there are more accurate ways to do inheritance but found this example interesting as to how it works exactly):
function CounterChild() {
Counter();
}
CounterChild.prototype = new Counter();
CounterChild.constructor = CounterChild;
var child = new CounterChild();
console.log(child.c); // 0
Here the this keyword relates to the object being instantiated, but how exactly does the call to the Counter() constructor get passed a reference to the object being created? I mean how does the interpreter know that this function should be passed a pointer at all as the new key word is not being used?
The most important thing to understand about
this
is that its value is determined by how the function called (expect for ES6 arrow functions, wherethis
is lexically scoped, and functions that are bound via.bind
).Calling
new Counter()
is very different from callingCounter()
and thusthis
refers to different values.Overall I recommend to read MDN -
this
for more information aboutthis
.Case 1
Yes
Case 2
Since the function is called "normally", i.e. as
foo()
,this
refers to the global object, which iswindow
in browsers. The code is equivalent toCase 3
It doesn't. Calling
Counter
insideCounterChild()
has no visible effect(*) here and you could simply remove it.The
c
property comes fromCounterChild.prototype.c
, since you assigned aCounter
instance toCounterChild.prototype
. You can verify this by inspectingconsole.dir(child)
.CounterChild.prototype
is the prototype of all instances created throughnew CounterChild()
. Any property that does not exist on an object itself is looked up in the object's prototype chain.If you want
this
insideCounter
to refer to the newly created object, you have to pass it explicitly:*: That's actually not true. Just like in case 2, it does change the global variable
c
, but that is definitely not what you want.