Namespacing with IIFE in ES6?
Asked Answered
O

4

8

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?

Ogawa answered 23/9, 2015 at 18:11 Comment(4)
Why would overwriting a global like that be something you have to worry about? You might as well do alert() anyway.Diminutive
@Diminutive because the 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.Ogawa
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.Diminutive
@Diminutive that makes sense. Thank you. Before its closing body tag, my index.html references a Babel compilation of scripts/main.js. Babel doesn't wrap my code in an IIFE for me. I updated my question.Ogawa
A
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.

Astronavigation answered 23/9, 2015 at 21:59 Comment(10)
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.Diminutive
You guys make sense, but Babel doesn't wrap my code in an IIFE if I use const or a block.Ogawa
@MattDiPasquale: Apparently it's because babel does only compile modules, not scripts: issue 1049Astronavigation
@Astronavigation oh, thanks! So then, what should I use for compiling scripts?Ogawa
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.Diminutive
@Astronavigation I think you need to update your closing syntax for your last example. }()); should be })();Iliac
@Justin: No it should not. Both are valid (and in ES6 even () => {…}() is), but I like it better to wrap the invocation instead of the function expression.Astronavigation
@Justin: Thanks, you were right. It's a shame that arrow functions are no primary expression like function expressions are…Astronavigation
@Astronavigation 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.Hemorrhoid
@GiacomoCerquone: But you usually are (should be) in strict mode?Astronavigation
J
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.

Januarius answered 23/9, 2015 at 18:18 Comment(2)
Thank you. So then, should I still create a namespace with an IIFE? See my updated question.Ogawa
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.Januarius
S
0

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

Stockyard answered 4/6, 2016 at 18:36 Comment(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.Denims
@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 ?Crevasse
A
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

Acrobatics answered 26/9, 2016 at 0:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.