-2

Executing this piece of code is different in the browser to the nodeJs.

let abc = (() => {
  function xyz() {
    this.man = "XYZ"
  }
  xyz();
  this.man = "ABC";
})()
console.log(man);

The answer expected is XYZ as produced in node but found ABC in the browser.

HacKaDoN
  • 27
  • 1
  • 5
  • Both Node (v15.5.1) and Browser (FFx on macOS) log `ABC`. – evolutionxbox Feb 10 '22 at 08:35
  • 1
    Both environments will depend upon whether you're running in strict mode and, in nodejs, you will be running in a module, not at the top scope. None of this really matters because this code is wrong for both environments as you're making poor assumptions about the value of `this`. Run this in strict mode instead of sloppy mode and it will be an error in both environments as it should be. Calling `xyz()` causes `this` to be undefined inside of `xyz()` in strict mode. Honestly, not really worth explaining bad code. Learn how not to write this type of code rather than how to explain it. – jfriend00 Feb 10 '22 at 08:36
  • `this.man = "ABC";` finally updated the global window object. Both `this.man = "XYZ"` and `this.man = "ABC";` update the global this – Amila Senadheera Feb 10 '22 at 08:38
  • In strict mode in the browser, this code throws an error. – evolutionxbox Feb 10 '22 at 08:40
  • @jfriend00 it also logs `ABC` in node too – evolutionxbox Feb 10 '22 at 08:41
  • @evolutionxbox - If you run it in sloppy mode which is just dumb these days. Run the code in strict mode and it throws in either the browser or in nodejs. This is sloppy code - no two ways about it. It makes implicit assumptions about the value of `this` which is just bad. It's bad code - not worth explaining. It would be immediately rejected in a code review. Better to learn how to not write bad code. It has a different effect in nodejs because the top level value for `this` in nodejs is the current module's `module.exports`, not the global object like it is in a browser. – jfriend00 Feb 10 '22 at 08:43
  • @jfriend00 you get no disagreement from me – evolutionxbox Feb 10 '22 at 08:48
  • Thanks for the clarity @jfriend00 I was just curious, for the reason " It has a different effect in nodejs because the top level value for this in nodejs is the current module's module.exports, not the global object like it is in a browser" this was really helpful for me. – HacKaDoN Feb 10 '22 at 09:00
  • It would be nice to add nodejs and browser versions in such type of questions – Oleg Flores Feb 10 '22 at 09:43

1 Answers1

3

First, a couple points:

  1. You should not write code like this that depends upon some implicit value of this that is inherited from the environment. If you mean to access the global object, then write code that explicitly references the global object.
  2. If you run this code in strict mode which was designed to prevent common programming mistakes and problems, this code will not run. It will throw an exception. This is a further hint that you shouldn't be writing code like this.

This code depends upon two defaults for the value of this.

First, it depends upon the top level value of this. In a browser that will be the window object. In a nodejs module, that will be the module.exports object for the current module.

Second, it depends upon what the value of this will be in your function call xyz(). In sloppy mode in the browser, that will be window. In sloppy mode in nodejs, that will be the global object. So, this.man = "XYZ" is setting a property on the global object. In strict mode, the value of this inside the xyz() function will be undefined.

Third, your assignment of this.man = "ABC" depends upon what the lexical value of this at the point of definition of your top level arrow function. In the browser, that will also be window. In nodejs, that will be module.exports.

So, in a browser, your xyz() function sets window.man to "XYZ", then it sets window.man to "ABC".

In nodejs, your xyz() function sets global.man to "XYZ" and then your arrow function sets module.exports.man to "ABC".


Lastly, run this in strict mode by inserting:

"use strict";

As the first line of code in the file and you will get an error:

TypeError: Cannot set properties of undefined (setting 'man')

because this inside of the xyz() function will be undefined (as it should be) and trying to set a property on undefined is a TypeError. You set properties on objects.

It is a great idea to run all your code in strict mode. More and more features in the language are automatically run in strict mode such as class methods because it's just better to run your code in strict mode. It doesn't prevent you from doing anything that there isn't a better way to do. It just prevents you from doing things (often accidentally) that you shouldn't be doing.


For a good illustration in nodejs, run this:

let abc = (() => {
    function xyz() {
        this.man = "XYZ";    // sets global.man
    }
    xyz();
    this.man = "ABC";        // sets module.exports.man
})()
console.log(this == module.exports);
console.log(module.exports.man);
console.log(global.man)

You will get this output:

true
ABC
XYZ

One other point here is that when you call a function such as xyz(), the value of this inside the execution of xyz() is determined by how the function is called. You can see an explanation of the various ways a function can be called here and how that affects the value of this inside the function.

Calling it as just a plain function xyz() in sloppy mode will set the value of this inside that function to the global object. Calling it as a plain function in strict mode will set the value of this inside that function to undefined.

jfriend00
  • 683,504
  • 96
  • 985
  • 979