A couple of things are going on here.
One is that when you run a script like node myScript.js
, the script is run as a "module". If your package.json has "type": "commonjs"
, or doesn't specify the "type"
field, it is run as a CommonJS module. If your package.json has "type": "module"
, it is run as a ECMAScript module. (related docs). That is, if you're using .js
as a file extension. If you use .cjs
, NodeJS will treat it as a CommonJS module, and if you use .mjs
, NodeJS will treat it as a ECMAScript module. (related docs).
See also 'this' different between REPL and script. The TL;DR is that with a CommonJS Module, the code in the module is run as if it were wrapped in the following:
var ctx = {};
(function(exports) {
// your code
console.log(this === globalThis); // false
}).call(ctx, ctx);
But if you run NodeJS in REPL mode (just run node
in your system console), then this === globalThis
is true
, and variables declared with var
at the "top level scope" will be placed in the globalThis
object, just like they are in the browser console (where globalThis
is synonymous with the global window
object).
In your browser console, you get "hi" because the function where you console.log(this.greeting)
is called without being given a specific this
, so it falls back to globalThis
. But in ECMAScript module contexts (Ex. NodeJS scripts run with a package.json with "type": "module"
, or web <script module>
(module scripts)), the this
at the top-level is always undefined
.
Another thing is that if you don't specify strict mode, JavaScript will typically be run in "sloppy mode" by default, where this
is never undefined
. Strict mode is usually opt-in, but in a ECMAScript module context, strict mode is the default. That's why you're getting undefined
when running node myScript.js
when you'd actually be getting an error if you did something to enable strict mode.
Here's what it would look like "behind the scenes" to run a CommonJS module with strict mode enabled (if you were to put "use strict";
at the top of your script file):
let ctx = {};
(function() {
"use strict";
function a(){
console.log(this.foo);
}
a(); // Uncaught TypeError: Cannot read properties of undefined (reading 'foo')
}).call(ctx);
You'd get the same error running the above code without explicitly using "use strict";
if you put "type": "module"
in your package.json.
More readings: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this.