5

In any web browser executing the following script will result in 'wee' being sent to the console. In Node it sends {}.

var d = 'wee';
console.log(this.d);

I realize that in Node this refers to the exports object in this case. I do know about the global variable, and that isn't what I'm trying to access. Besides, the script above does not set d on the global object either. Where the hell does it go? I can access it explicitly by console.log(d); in the script above but it appears to be stashed away in some nonstandard space for no good reason at all.

I also realize that removing the var will declare d on the global object, which is the expected behavior although it seems stupid to have var at the top level scope storing its values in a different place than "naked" variables. I mean, isn't the point of the module system supposed to be some sort of digital prophylactic to guard against global pollution? Here it seems so easy to break the pattern and so difficult to do something standard.

d is not declared on the module object either.

I don't have to justify why I'm asking this question but I'll answer the first troll to come along with a "but why do you want to do taht hurr durrrr".

var d = {};
d.bleep = 'y';
var a = Object.keys(d);

d.bloop = 'y';
d.blop = 'y';

var b = Object.keys(d);

// c = b - a;
var c = b.filter(function (item) {
    if(a.indexOf(item) === -1) {
        return true;
    }
    return false;
});

console.log(a,b,c);

In the same way that I can differentiate between certain object states of d, I should be able to differentiate the states of the top level scope. In a browser this is the window object, referred to by this in the top level scope. I should be able to asses the properties of the environment before and after script execution to determine a great many things, one of which would be inspection of functions and variables declared in the top scope of an arbitrary script that they may then be applied to the exports object. This would make it easy to programatically generate module wrappers for scripts which were not written as modules with a simple forEach applied to the list of top level functions and variables to assign whateverThisIs['varFunc'] to module.exports['varFunc']...

and stuff...

This behavior appears to be like that of an anonymous function. In an anonymous function this could refer to the window object, vars would have to be called directly ( as they're in the anon func's scope ) and, leaked vars declared without the var keyword could end up at the window object. I haven't yet read the entire manual, maybe this is exactly what is going on but, I was under the impression that each module executed in it's own context (window) and that Node passed messages between module contexts through the use of global and module.exports...

I don't know. I want to know though. If you know, let me know.

Kastor
  • 597
  • 4
  • 14
  • 2
    It goes in the local variable scope. In a module, your code is wrapped in a function. You just don't see it,. – the system Mar 08 '13 at 05:20
  • So it is just an anonymous function then? For sure? – Kastor Mar 08 '13 at 05:24
  • 2
    Yes... well I don't remember if it's anonymous, but that doesn't matter. It is a function. The first parameter defined for the function is `exports`, and an empty object is passed to that parameter. The same object is set as the `this` value of the function. You can test this with `console.log(arguments[0] === exports, arguments[0] === this);`, and you'll get `true` for both. – the system Mar 08 '13 at 05:27
  • 2
    ...basically `eval("var exp = {}; (function(exports) {" + "/* your code */" + "}).call(exp, exp);")`, Except there's a bit more to it than that of course. – the system Mar 08 '13 at 05:29
  • Thank you. :D At least now I know the only way to get the local top level scope would be to hack the source. :D – Kastor Mar 08 '13 at 05:34
  • See also [Why does a module level return statement work in Node.js?](http://stackoverflow.com/q/28955047/1048572) – Bergi Mar 02 '16 at 17:00

1 Answers1

11

So every node module is wrapped as the body of a function as shown here in the node source code

NativeModule.wrapper = [
  '(function (exports, require, module, __filename, __dirname) { ',
  '\n});'
];

So if you declare a variable with var, it is function-local to the module, basically a private variable for that module. It is not a property of global, module, module.exports, or this. If you forget var, it goes into the global object as a property. If you explicitly create a property on this, that goes into exports and is available to other modules.

Here's a little program that is hopefully enlightening.

var aDeclaredVar = '*aDeclaredVar*';
undeclaredVar = '*undeclaredVar*';
this.aThisProperty = '*aThisProperty*';
module.aModuleProperty = '*aModuleProperty*';
module.exports.anExportProperty = '*anExportProperty*';

console.log('this', this);
console.log('this === exports', this === exports);
console.log('this === module', this === module);
console.log('this === module.exports', this === module.exports);
console.log('aDeclaredVar', aDeclaredVar);
console.log('undeclaredVar', undeclaredVar);
console.log('this.aThisProperty', this.aThisProperty);
console.log('module.aModuleProperty', module.aModuleProperty);
console.log('module.exports.anExportProperty', module.exports.anExportProperty);
console.log('global.undeclaredVar', global.undeclaredVar);
console.log('global.aDeclaredVar', global.aDeclaredVar);

And it's output:

this { aThisProperty: '*aThisProperty*',
  anExportProperty: '*anExportProperty*' }
this === exports true
this === module false
this === module.exports true
aDeclaredVar *aDeclaredVar*
undeclaredVar *undeclaredVar*
this.aThisProperty *aThisProperty*
module.aModuleProperty *aModuleProperty*
module.exports.anExportProperty *anExportProperty*
global.undeclaredVar *undeclaredVar*
global.aDeclaredVar undefined
Peter Lyons
  • 131,697
  • 28
  • 263
  • 265
  • Thanks for the link to the source. :D I never tried introspecting the local vars of a function before. I don't know if there's a hack to do it or not but for sure the behavior I see makes good sense now that I know the context. It's like writing bookmarklets. – Kastor Mar 08 '13 at 06:08
  • so i tried running a script that would end the wrapper... }; I'm going to find something else to do. :D – Kastor Mar 08 '13 at 06:27
  • Thanks for clearing this up...was writing a native node extension and was struggling with this for a couple of days! +1 – jkp May 24 '13 at 17:54
  • Wow this was so much help! – Abdullah Rasheed Nov 11 '15 at 17:06