static variables within javascript functions and how it works under the hood

1.3k views Asked by At

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?

1

There are 1 answers

0
Felix Kling On BEST ANSWER

The most important thing to understand about this is that its value is determined by how the function called (expect for ES6 arrow functions, where this is lexically scoped, and functions that are bound via .bind).

Calling new Counter() is very different from calling Counter() and thus this refers to different values.

Overall I recommend to read MDN - this for more information about this.


Case 1

Yes

Case 2

Since the function is called "normally", i.e. as foo(), this refers to the global object, which is window in browsers. The code is equivalent to

function counter() {
    window.c = window.c || 1;
    return window.c++;
}

Case 3

how exactly does the call to the Counter() constructor get passed a reference to the object being created?

It doesn't. Calling Counter inside CounterChild() has no visible effect(*) here and you could simply remove it.

The c property comes from CounterChild.prototype.c, since you assigned a Counter instance to CounterChild.prototype. You can verify this by inspecting console.dir(child).

CounterChild.prototype is the prototype of all instances created through new CounterChild(). Any property that does not exist on an object itself is looked up in the object's prototype chain.

If you want this inside Counter to refer to the newly created object, you have to pass it explicitly:

function CounterChild() {
    Counter.call(this);
}

*: 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.