Which "this" is This in a Node Module?
Tags: javascript, node
Published: 2021-10-05 01:23:25

Understanding which object context the keyword `this` is referencing is one of the more confusing language features in JavaScript. Even if you have a firm understanding of the rules for `this` binding there is still opportunity for it to surprise you, like when using `this` in the outermost scope of a Node module. 

For comparison purposes, let's see what happens when we use `this` in the outermost scope of a script run in the browser before we try using it in a Node module.

```html
<html>
<head>
    <title>Which This Is This</title>
</head>
    <body>
    <script>
        this.context = "is this the global context?"

        function whichThisIsThis() {
            alert(this.context)
        }

        whichThisIsThis()
    </script>
    </body>
</html>
```

Opening the above HTML in a browser creates an alert modal with the text "is this the global context?" displayed, confirming that both references to `this` are for the same context object. If we replace `this.context = ...` with `window.context = ...` in the first line of the script we get the same text in the alert modal as before, confirming that `this` is the `window` object when `whichThisIsThis` is executed.

Based on the above experiment in the browser, what do you think the following code will print to the console if executed by the Node runtime?

```javascript
this.context = "is this the global context?"

function whichThisIsThis() {
    console.log(this.context)
}

whichThisIsThis()
```

Surprisingly, it will print `undefined`. Unlike in the browser script, `this` in the outermost scope of a Node module is not a reference to the global object, rather it is a reference to the `module.exports` object. 

However when `whichThisIsThis` is executed in the example above, `this` inside the function's scope _is_ the global object. Because the outermost scope is setting a `context` property on the `this` that's referencing `module.exports`, we get an undefined response when trying to access a `context` property on the `this` that references the global object, the `this` inside the `whichThisIsThis` function scope.

You can test this out by changing `this.context` in the call to `console.log` to `module.exports.context`. Run the code again and you should see the string value assigned to `this.context` in the outermost scope print to the console.

```javascript
this.context = "this is the module.exports context, not the global context!"

function whichThisIsThis() {
    console.log(module.exports.context)
}

whichThisIsThis()
```

However if your intention is to use the global object you can keep `this.context` as the argument for `console.log` but you will need to explicitly create and set a `context` property on the global object. 

```javascript
global.context = "this is the global context!"

function whichThisIsThis() {
    console.log(this.context)
}

whichThisIsThis()
```

Keep in mind that the `this` in the `whichThisIsThis` function is only referencing the global object in the above example because no other context has been provided. If this function were to be called in a way that implicitly or explicitly provided a different object to assume the role of `this`, the outcome would be different.

```javascript
global.context = "default global context"

function whichThisIsThis() {
    console.log(this.context)
}


let implicitContext = {
    context: "implicitly bound context",
    whichThisIsThis
}
implicitContext.whichThisIsThis()


let explicitContext = whichThisIsThis.bind({context: "explicitly bound context"})
explicitContext()
```

Running the above code will print "implicitly bound context" and "explicitly bound context" to the console but not "default global context".