9

Apparently, ES6 doesn't need namespacing because each file is a separate module.

But then, how do I avoid global namespace interference?

For example, Babel compiles my scripts/main.js file by merely replacing const with var.

var alert = 'This line doesn\'t do anything.'
window.alert(alert)

A namespace (named ANS below) with an IIFE prevents name collisions:

const ANS = (function () {
  const alert = 'This works'
  window.alert(alert + '.')
  return {alert: alert + ' too.'}
})()
alert(ANS.alert)

Adding properties to the namespace ANS is cleaner than adding them to the global namespace, window, or this. And, the IIFE provides further encapsulation.

So, isn't the second way, i.e., creating a custom namespace with an IIFE, better than the first? If so, is there a newer/nicer way of doing this in ES2015? Why doesn't Babel do this for me?

ma11hew28
  • 121,420
  • 116
  • 450
  • 651
  • Why would overwriting a global like that be something you have to worry about? You might as well do `alert()` anyway. – loganfsmyth Sep 23 '15 at 18:55
  • @loganfsmyth because the [Global Object](http://www.ecma-international.org/ecma-262/6.0/#sec-global-object) `window` has lots of properties. My app will have some too, and I want to avoid potential name collisions. Please see my updated question. – ma11hew28 Sep 23 '15 at 21:57
  • Maybe there is some miscommunication on this question. Is your concern in the first example that `alert === window.alert`? If so, that is not the case in a true ES6 environment, though it might with a transpiler. It definitely isn't the case when using ES6 modules with a proper module bundler. – loganfsmyth Sep 23 '15 at 22:08
  • @loganfsmyth that makes sense. Thank you. Before its closing `body` tag, my `index.html` references a [Babel](https://babeljs.io) compilation of `scripts/main.js`. Babel doesn't wrap my code in an IIFE for me. I updated my question. – ma11hew28 Sep 24 '15 at 11:03

4 Answers4

9

Apparently, ES6 doesn't need namespacing because each file is a separate module.

Not exactly. Each module does have its own scope, that is correct, but not every file is a module. There still are scripts in ES6 that work just like those from ES5, and are executed in the global scope.
In those scripts, you still need to avoid globals as much as you can, typically by not declaring/assigning any variables or by wrapping your "module" in an IEFE to give it a separate variable scope.

Is there a newer/nicer way of doing this in ES6?

You can use a block and lexical variable declarations (let, const, function):

{
    const msg = 'This line doesn\'t do anything.'
    window.alert(msg);
}
// msg is not defined here

Or you can use an arrow function in your IEFE, which allow you to use this to refer to the global object without needing to use .call(this)):

(() => {
    var msg = 'This line doesn\'t do anything.'
    window.alert(msg);
})();

But then, how do I avoid global namespace interference, or name collisions?

In ES6 modules, there is nothing global except the builtin objects and maybe the global object. Avoid modifying them.

And of course you will need to take care about collisions between module names - how to do that should be explained in the docs for the resolver mechanism of your module loader.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • It's my understanding that scripts in ES6 have a shared lexical environment, so while `var alert = 'blah';` would be equivalent to `window.alert = 'blah';`, `let alert = 'blah'` and `const alert = 'blah`;` would create a globally lexically scoped variables without changing `window.alert`. Harder to transpile that though. – loganfsmyth Sep 23 '15 at 22:16
  • You guys make sense, but [Babel](https://babeljs.io) doesn't wrap my code in an IIFE if I use `const` or a block. – ma11hew28 Sep 24 '15 at 11:11
  • @MattDiPasquale: Apparently it's because babel does only compile modules, not scripts: [issue 1049](https://github.com/babel/babel/issues/1049) – Bergi Sep 24 '15 at 11:32
  • @Bergi oh, thanks! So then, what should I use for compiling scripts? – ma11hew28 Sep 24 '15 at 15:04
  • 2
    Is there are reason you want to avoid modules? Most people use Webpack or browserify to bundle all of their module code into a single script. – loganfsmyth Sep 24 '15 at 16:52
  • @Bergi I think you need to update your closing syntax for your last example. `}());` should be `})();` – Justin Nov 20 '15 at 18:17
  • @Justin: No it should not. [Both are valid](http://stackoverflow.com/q/3384504/1048572) (and in ES6 even `() => {…}()` is), but I like it better to wrap the invocation instead of the function expression. – Bergi Nov 20 '15 at 19:13
  • @Justin: Thanks, you were right. It's a shame that arrow functions are no primary expression like function expressions are… – Bergi Dec 17 '15 at 16:22
  • @Bergi Actually there would be no need to use `.call(this)`. If you're not in strict mode and you have an IIFE `this` automatically refers to the window object. – Giacomo Cerquone Mar 20 '16 at 21:01
  • @GiacomoCerquone: But you usually are (should be) in strict mode? – Bergi Mar 20 '16 at 21:24
2

But, how do I prevent overwriting globals from within a module?

You can't. Nothing changed in this regard compared to ES5. The advice still is: Avoid using globals.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Thank you. So then, should I still create a namespace with an IIFE? See my updated question. – ma11hew28 Sep 23 '15 at 21:48
  • IIFE are predominantly used to create scope. Modules already have there own scope so there is no need to use an IIFE in a module (unless of course you want to do that within the module itself). How you organize or expose your data in/from the module is up to you. – Felix Kling Sep 23 '15 at 22:39
1

Apparently, ES6 doesn't need namespacing because each file is a separate module.

This is misinformation. As far as I know, only files that use export are ES6 modules. Without export, an ES6 file is just an ordinary script that has access to the global scope unless it is either wrapped in an IIFE or is transformed during a build process.

Revision (12 July 2016): Apparently, I corrected misinformation with more misinformation. As Kyle Simpson clarifies below, "According to the current spec, what makes a file an ES6 module is how the environment (node, browser, etc) chooses to load it, not its contents."

In You Don't Know JS: ES & Beyond, Kyle Simpson, a.k.a. @getify, states

"Modules do still have access to window and all the "globals" that hang off it, just not as lexical top-level scope. However, you really should stay away from the globals in your modules if at all possible."

Now to your questions.

[I]sn't the second way, i.e., creating a custom namespace with an IIFE, better than the first?

That depends on the environment in which your module is running. For example, in node, there is no Window object; therefore, in that environment there is no need to worry about your modules polluting the global namespace.

On the other hand, in the browser, Window is a global web API, and your module does have access to it. Technically, anything your module attaches to Window is not, strictly speaking, global; however, anything that is attached to Window is so close to a global that it is generally considered to be a bad practice to modify it except through Window's own API.

[H]ow do I avoid global namespace interference?

and

[I]s there a newer/nicer way of doing this in ES2015?

I don't know what the best practices are for dealing with scope in ES6. For the time being, I do the following:

  • If a file is a module, it's top-level scope is the file itself, so I don't concern myself with global scope.
  • If a file is not a module, I wrap it in an IIFE.

Source: You Don't Know JS: ES & Beyond Also Recommended: ES6 In Depth: Modules by Jason Orendorff

jfmercer
  • 3,641
  • 3
  • 29
  • 35
  • 2
    Clarification: "only files that use export are ES6 modules". Not exactly. According to the current spec, what makes a file an ES6 module is how the environment (node, browser, etc) chooses to load it, not its contents. – Kyle Simpson Jul 12 '16 at 04:59
  • @jfmercer, you wrote.. "if a file is a module..." and "if a file is not a module...".. please clarify what exactly makes a file a module and not a module ? – joedotnot Oct 30 '19 at 07:53
0

Stumbled here through Googling the same issue. Assuming you use a module loader such as rollup, try adding this line to your rollup options:

format: 'iife'

Src: https://github.com/samccone/The-cost-of-transpiling-es2015-in-2016/blob/master/rollup-plugin-babel/rollup.config.js#L9

Docs: https://github.com/rollup/rollup/wiki/JavaScript-API#format

Scott L
  • 533
  • 5
  • 9