49

I have a JavaScript file which is loaded by require.

// loaded by require()

var a = this; // "this" is an empty object
this.anObject = {name:"An object"};

var aFunction = function() {
    var innerThis = this; // "this" is node global object
};

aFunction();

(function(anyParameter){
    console.log(anyParameter.anObject);
})(
    this // "this" is same having anObject. Not "global"
);

My question is: this in var a = this; is an empty object whereas this statements in functions are shadows of node.js global object. I know this keyword is different in functions but I could not understand why first this is not equal to global and this in functions equals to global.

How does node.js inject global to this in function scopes, and why it does not inject it to the module scope?

Gökçer Gökdal
  • 861
  • 1
  • 10
  • 18
  • 4
    The value of `this` is determined as a core feature of the JavaScript language itself (although NodeJS may set the value via the language features of JavaScript). You might need to read up on some [`this` docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this). Note that the docs generally assume the JS is running in a browser, so the "global object" will be `window` instead of the NodeJS global object, but the concepts are the same. Some more relevant docs are [NodeJS's `this` docs](http://howtonode.org/what-is-this). – ajp15243 Mar 31 '14 at 19:29
  • 1
    Have a look at [this question](http://stackoverflow.com/questions/3127429/javascript-this-keyword). – Two-Bit Alchemist Mar 31 '14 at 19:34
  • 1
    I know why two `this` values are different. My question is why and how node.js injects `global` to `this` in function scope, not to outer scope. It can inject `global` to both `this`'es keeping them different. – Gökçer Gökdal Mar 31 '14 at 19:35
  • How do you call `aFunction`? – ajp15243 Mar 31 '14 at 19:42
  • @ajp15243 Document you pointed gave much more insight about `this`, thanks. As I understood `this` becomes different according to where function is called. But still I could not find out why and how node.js injects `global` in different ways. Note: I have added call for `aFunction` like when I debugged. – Gökçer Gökdal Mar 31 '14 at 19:47
  • 1
    @GökçerGökdal: I think [this answer](http://stackoverflow.com/a/19850472/2359560) at least partially answers your question. – go-oleg Mar 31 '14 at 19:51

4 Answers4

84

Here's a few fundamental facts you must understand to clarify the situation:

  • In the top-level code in a Node module, this is equivalent to module.exports. That's the empty object you see.

  • When you use this inside of a function, the value of this is determined anew before each and every execution of the function, and its value is determined by how the function is executed. This means that two invocations of the exact same function object could have different this values if the invocation mechanisms are different (e.g. aFunction() vs. aFunction.call(newThis) vs. emitter.addEventListener("someEvent", aFunction);, etc.) In your case, aFunction() in non-strict mode runs the function with this set to the global object.

  • When JavaScript files are required as Node modules, the Node engine runs the module code inside of a wrapper function. That module-wrapping function is invoked with a this set to module.exports. (Recall, above, a function may be run with an abitrary this value.)

Thus, you get different this values because each this resides inside a different function: the first is inside of the Node-created module-wrapper function and the second is inside of aFunction.

Community
  • 1
  • 1
apsillers
  • 101,930
  • 15
  • 206
  • 224
  • 1
    I think I also found the code where module.export comes. In module.js (node.js source) execution code uses javascript `apply` method as `return compiledWrapper.apply(self.exports, args);`. Thanks a lot to everybody for all your helps. – Gökçer Gökdal Mar 31 '14 at 20:40
  • Wow! Very clear answer. – Rahul Purohit Jan 19 '21 at 15:43
35

To understand this, you need to understand that Node.js actually wraps your module code in to a function, like this

(function (exports, require, module, __filename, __dirname) {
    var test = function(){
        console.log('From test: '  + this);
    };
    console.log(this);
    test();
});

Detailed explanation can be found in this answer.


Now, this wrapped function is actually invoked like this

var args = [self.exports, require, self, filename, dirname];
return compiledWrapper.apply(self.exports, args);

So, this, at the module level, is actually the exports object.

You can confirm that like this

console.log(this, this === module.exports);
// {} true
Community
  • 1
  • 1
thefourtheye
  • 206,604
  • 43
  • 412
  • 459
  • 1
    Probably worth pointing out that `test` could be called in different ways, setting `this` to something else – Juan Mendes Jun 17 '15 at 14:47
  • 1
    From what I understand calling a function like `fn()` will make `this` point to the global object, or null when using `use strict`, it doesn't matter what the current `this` is... is Node different from the browser when it comes to setting `this`? I think the correct answer is just that the global object is set to `exports` – Juan Mendes Jun 17 '15 at 14:49
  • Care to comment on my comment (and my answer?), I think your answer is misleading because the value of this where test() is being called doesn't affect the value of this inside test(), it's all about how it's called. I'd love to understand if I'm missing something – Juan Mendes Jun 17 '15 at 15:08
11

Summary:

In Javascript the value of this is determined when a function is called. Not when a function is created. In nodeJS in the outermost scope of a module the value of this is the current module.exports object. When a function is called as a property of an object the value of this changes to the object it was called. You can remember this simply by the left-of-the-dot rule:

When a function is called you can determine the value of this by looking at the place of the function invocation. The object left of the dot is the value of this. If there is no object left of the dot the value of this is the module.exports object (window in browsers).

caveats:

  • This rule does not apply for es2015 arrow function which don't have their own binding of this.
  • The functions call, apply, and bind can bend the rules regarding the this value.

Example (NodeJS):

console.log(this);  // {} , this === module.exports which is an empty object for now

module.exports.foo = 5;

console.log(this);  // { foo:5 }

let obj = {
    func1: function () { console.log(this); },
    func2: () => { console.log(this); }
}

obj.func1();  // obj is left of the dot, so this is obj
obj.func2();  // arrow function don't have their own this
              // binding, so this is module.exports, which is{ foo:5 } 

Output:

enter image description here

Willem van der Veen
  • 19,609
  • 11
  • 116
  • 113
-1

It's because the default global object in a Node.js module is the exports object, and you are calling test() which doesn't specify this. In traditional JS, this points to the global object, with use strict, this will be null.

this can point to anything, it just depends on how you call it.

  • test(): Uses the global object (exports) as this, unless in strict mode, where this will be null;
  • test.call({}) or test.apply({}): You are specifying what to use as this (the first parameter)
  • var obj = {testRef: test}; obj.testRef(): this is set to the left of the ., that is, obj

Countering thefourtheye's answer

It is true that this in the top level of the module is exports, but that doesn't necessarily mean that this inside test() will also point to same thing as where it was called from.


Attempting to prove that this and the global object both point to exports

 myGLobal = 5;
 this.myGlobal; // 5
Community
  • 1
  • 1
Juan Mendes
  • 80,964
  • 26
  • 138
  • 189
  • 3
    Sorry for correcting you, but `this` points to `global` in [REPL](https://nodejs.org/api/repl.html) mode only – Oleg Jun 17 '15 at 15:24
  • 1
    `global object in Node.js is the exports object` - It is not correct. Each and every module will have its own `exports` object. – thefourtheye Jun 17 '15 at 16:02
  • @Curious Thanks, that's why I asked, I ran my test from the command line – Juan Mendes Jun 17 '15 at 17:04
  • @thefourtheye I did gather that from the documentation, does that mean that there are no globals? In my example above, would myGlobal not exist in a different module? – Juan Mendes Jun 17 '15 at 17:05
  • 2
    @JuanMendes globals do exist in Node, but unlike client-side JS, you should access `global` object. For example, `global.myName = 'Mike';` will be seen in all modules via `global.myName` or just `myName`. But `var myName = 'Mike';` or `myName = 'Mike'` is only seen in current module. Take a look at the [docs](https://nodejs.org/api/globals.html#globals_global) – Oleg Jun 17 '15 at 17:32
  • @JuanMendes you pointed that because of the 'use strict', 'this' will be null when we invoke the function 'test'. In Node.js, by default, the 'use strict' mode is on? – Rodrigo Branas Mar 29 '16 at 22:34
  • In non-strict mode, `this` is _not_ `null` in a function invoked without a calling context. It is `undefined`. – Patrick Roberts Aug 18 '17 at 22:41