Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why 'this' declared in a file and within a function points to different object in Node.js [duplicate]

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?

like image 406
Gökçer Gökdal Avatar asked Mar 31 '14 19:03

Gökçer Gökdal


3 Answers

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.

like image 181
apsillers Avatar answered Oct 21 '22 07:10

apsillers


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
like image 42
thefourtheye Avatar answered Oct 21 '22 06:10

thefourtheye


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

like image 39
Willem van der Veen Avatar answered Oct 21 '22 05:10

Willem van der Veen