2

I'm studying about the 'this' keyword inside regular function and arrow function, so following examples in Stack Overflow I think that I had understood that, but one detail left unsolved for me. Please help me with that. I don't know why but when I test the below code using node myScript.js the result is undefined, while using Fiddle or Chrome or Firefox the result is the expected.

As I had read, because of the fo2 function is called without bind any object so its 'this' keyword refers to global object, and 'hi' should be printed. It doesn't happen using node myScript.js but yes using Browser Console or Fiddle. I don't use console.log() inside the functions to avoid returning undefined, as it has been explained before in other Stack Overflow answers.

This is the code;

var greeting = 'hi';

const obj = {
  greeting: 'hey',

  fo() {
    const greeting = 'hello';

      function fo2 () {
        return this.greeting;
      };
      
      return fo2();  
  }  
};

console.log(obj.fo()); // 'hi' using browser console and Fiddle, but undefined using `node myScript.js`

I'm using NodeJS 18.12.1.

starball
  • 20,030
  • 7
  • 43
  • 238
andres_rom
  • 23
  • 3
  • Just want to confirm that you understand that you're invoking `fo2()` without providing an object. compare your `return fo2()` to `return this.fo2()` – Wyck May 01 '23 at 04:29
  • @Wyck indeed, that'd make a difference. But let's please not change the question at this point, which clearly asks about what's causing "hi" in one context, and "undefined" in another. If that's a mistake, that should be its own question. – starball May 01 '23 at 04:33
  • @Wyck Thanks to the answer of **user**, i was doing some changes, just for curiosity, trying to get the correct out in node terminal (**fo2()** bound the global object) and it got there using **"type":"commonjs"** in package.json and assigning the _greeting_ top variable like **globalThis.greeting = 'hi'**. – andres_rom May 01 '23 at 18:46

1 Answers1

4

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.

starball
  • 20,030
  • 7
  • 43
  • 238