Search code examples
javascriptnode.jsjsfiddle

JavaScript let binding should not be added to global object, different behaviour in browser vs Node vs jsfiddle


In the MDN documentation about let I find the following piece of text:

"At the top level of programs and functions, let, unlike var, does not create a property on the global object."

And accompanying sample code:

var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined

When I add this code to an HTML file like so:

<html>

<head>
  <script type="text/javascript">
    var x = 'global';
    let y = 'global';
    console.log(this.x); // "global"
    console.log(this.y); // undefined
  </script>
</head>

<body>
  <h1>foo</h1>
</body>

</html>

I get the expected output in the console:

global
undefined

However, if I run the code in node I get:

undefined
undefined

And if I run the same code in JSFiddle I also get:

undefined
undefined

Why is this the case? Does the global object act differently in different runtime environments? Where is this behaviour documented?

Or is "the top level of programs" something different in these situations?

Thanks!


Solution

  • The top level is indeed different in node. Try the following:

    // get access to the global object e.g. window, global
    var g = (new Function('return this;'))();
    var x = 1;
    let y = 2;
    console.log(typeof x); // 'number'
    console.log(typeof y); // 'number'
    console.log(typeof g.x); // 'undefined'
    console.log(typeof g.y); // 'undefined'
    

    The reason that your code 'works' in node is that in node.js the top level is scoped to the module, not the global environment like browsers (which is a great feature of node.js that the browsers can't add for legacy reasons). So your let statement creates a module-level binding. However if you were to import that module from another module:

    require('./path/to/module/with/xandy');
    console.log(typeof x); // 'undefined'
    console.log(typeof y); // 'undefined'
    

    The binding isn't shared across the module boundary.

    As for the fiddle, I can only assume that they're doing some sort of sandboxing that prevents your fiddle code from working as expected.