If Babel compiles all let and const to var couldn't this cause problems?

Hello.

I’ve been following some ES6 tutorials.

When I’ve finished writing my code I’ve been putting it into the online https://babeljs.io page to see how it gets compiled.

Here’s an example:

ES6

var     userId = 1
let     postId = 200
const   siteId = 4;

console.log(window.userId) // returns 1
console.log(window.postId) // returns undefined
console.log(window.siteId) // returns undefined

Babel compiled:

var     userId = 1
var     postId = 200
var     siteId = 4;

console.log(window.userId) // returns 1
console.log(window.postId) // returns 200
console.log(window.siteId) // returns 4

The above ES6 code is from a tutorial explaining how var attaches itself to the Global Window Object, and how this might be a bad thing. let and const do not attach themselves.

But if we use Babel then let and const get changed to var. So now our variables will be attached to the Global Window Object.

Same with Block Scope. let and const recognise it, but var does not. Once compiled though, everything is a var and Block Scope ceases to exist!

I suspect I’m missing something here - I must be - can anyone enlighten me?

:slight_smile:

1 Like

Hey there! You are definitely right that these still become global variables when let and const become var after being transpiled with Babel. This is part of the reason a lot of ES5 code used the IIFE pattern – https://developer.mozilla.org/en-US/docs/Glossary/IIFE.

This Stack Overflow discussion helps shed some light on it as well: https://stackoverflow.com/questions/32746615/namespacing-with-iife-in-es6

In general, though, avoiding globals is good, and there are patterns to help with that.

Hi - thanks for the info!

What are your thoughts on the loss of Block scope after compilation?

I think that this isn’t an issue when using Babel because Babel is most commonly used with Webpack. Webpack wraps its code in an IIFE. If you’re just playing around with it locally, then there’s probably not a reason to target something like ES5 unless you’re just curious. The browser support for native let and const is pretty widespread: https://kangax.github.io/compat-table/es6/

Let me know if you have any other questions!

So, are you saying that:

  1. Webpack , after Babel has compiled ES6 to ES5, wraps the entire code (or selected parts of the code) in IIFEs - and that this successfully deals with not only global variables but block scope, and

  2. the Try it Yourself online Babel page, where you can paste ES6 code in one window and see it immediately compiled to ES5 in another window is not using Webpack (and so I don’t get to see any of those IIFEs)?

If this is so, then my previous question “I suspect I’m missing something here - I must be…?” will have been answered!

Thanks for your help,
Chris

I show two examples - 1) the ES6 code, 2) the ES5 code, compiled by Babel.

In the ES6 code only the var is in global window scope.

The compiled ES5 code changes let and const to var so all three variables are now in global window scope.

First off, great question. This is exactly the kind of forum post I crave. Thanks! Secondly, to add onto what @camperextraordinaire said, the problem you don’t seem to be addressing is that in your example, the scope of all the top-level variable declarations is the “global environment.” It’s a little confusing, and I had to learn it myself, but to summarize the top answer from this StackOverflow question

  • let and const are keywords that bind values to symbols (i.e.: declare variables) in a lexically-scoped way.
  • var is a keyword that binds values to symbols in a global way.
  • Even if a globally-declared variable is declared using let or const, that binding is recorded in one of two parts of the global environment record: the declarative environment record, as opposed to globally-declared var variables, whose bindings are stored in the other part, the object environment record
    • To think of this more concretely: the “global object” is basically window, and you can access anything that is “hung on” the window in the object environment record via the member access operator . as in window.myVar. All the global let and var stuff is not part of window's object environment record, so it can’t be accessed as a member from outside. It’s still there, but on the declarative environment record, which can only be accessed from inside itself by directly typing the variable name (myLet, e.g. not window.myLet) so that’s how lexically-scoped declarations are kept separate from global declarations.

I wrote a codepen for you to see this in action. I even used var inside a function declaration to show you that even before ES6/ES2015, function-scoping was a thing, and it works by having a separate declarative environment record on a per-function basis, which is why the IIFE thing with webpack works. (Thanks, @johnstonbl01). Code repeated here:

var varX = 1;
let letX = 2;
const constX = 3;

function myFunc(){
	var 	userId = 1;
  var   postId = 200;
  var   siteId = 4;
  console.log("accessing function-scoped var declarations from within function scope:", userId, postId, siteId);
}

console.clear();
console.log("accessing variable on global object environment record:", window.varX, window.letX, window.constX);//1, undefined, undefined
console.log("accessings variables using global (object + declarative) environment record", varX, letX, constX); //1, 2, 3
console.log("accessing non-global (function-scoped) var declarations:", window.userId, window.postId, window.siteId); //undefined, undefined, undefined
myFunc();
console.log("accessing non-global (function-scoped) var declarations [same after invocation]:", window.userId, window.postId, window.siteId); //undefined, undefined, undefined

2 Likes

Hi Vipatron,

thanks for the answer - I understand what you’re saying.

I did say, in my original post that " var attaches itself to the Global Window Object, and how this might be a bad thing. let and const do not attach themselves.". I do understand that those variables (var, let, const) are in the Global environment.

My concern was that Babel simply translated lets and consts into vars thereby attaching them to the Global Window Object.

I also understand that var had function scope, but not block scope. So I was worried that Babel, by compiling all lets and consts to vars would abolish block scope.

And the main thing I’m trying to ascertain is that if I write ES6 with global environmental lets and consts, and also write Block scoped variables Webpack will take care of all this during compiling and my code will run as I originally wrote it!

BTW I do understand that avoiding global variables is good practice.

Thanks again for your detailed reply!

Totally. Which is why I agreed that this is a great thread. @johnstonbl01 asserted that Webpack wraps code in an IIFE, which solves the let/const problem a different way.

To your main point: I’m willing to accept that assertion simply based on proof by negation. If (Babel+webpack) didn’t somehow preserve block scope, some of my React projects wouldn’t work correctly. But I know they do, ergo (Babel + Webpack) does preserve block scope.

I truly hope you didn’t think I was talking down to you. The level of a question that causes me this much skull-sweat is no insult to you. My teaching background makes it a habit of my to get background-material covered tersely to get into the real meat. I fear that you might have missed my point, and should follow that StackOverflow link. You said:

" var attaches itself to the Global Window Object, and how this might be a bad thing. let and const do not attach themselves."

What I learned today is that both types of declarations, var and let/const, result in bindings attached to the Window object (inside the global environment object), but that only var attaches itself to a sub-object of the global environment object, the object environment record, whose members can be accessed as members of window. It’s a fine point, and doesn’t answer your main question, but does answer the point that @camperextraordinaire raised about your example code. So, let’s declare that answered, and focus on your main question.

The best I’ve got for you is proof by negation. I understand your trepidation to place your entire weight on that, but I simply don’t know enough about webpack to answer if/how the IIFEs are generated.

OK now I get it properly. I do have a problem getting the terms right (but without the right terms confusion ensues, so thanks).

And no, I don’t think you’re speaking down to me. I much prefer a precise and detailed explanation to a laconic one liner.

Finally, your proof by negation works for me :slight_smile:

1 Like

BTW read the stack overflow page - very good.

On another stack overflow found this:

Both let and var variables, if declared at the top-level of a script, are accessible outside of the script file. However, only var variables get assigned to the window object.

I realise that your formulation is a lot stricter though!

1 Like

And just so that we’re not talking about proof by negation… here’s a Webpack bundle that has not been uglified:

/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/

(function (modules { ... is the IIFE. All of your code modules are defined within that scope.

1 Like

@johnstonbl01, the hero the city deserves.

1 Like

Great, thanks - I shall code with tranquility :slight_smile:

2 Likes