const are especially important, as they allowed for better control over variables and scoping.
Another celebrated new feature was the introduction of block scoping, which allows you to wrap any arbitrary piece of code in braces to indicate, well, a block scope.
However, there are a series of conditions that need to be met in order to fulfill block scope’s destiny to become the fill-in for the awkward IIFE.
Out of the box, if you were to use block scope right now on Chrome, Safari, or Firefox (and likely other browsers and Node.js), there are some problems with block scope being leaky.
These issues begin to emerge when you attempt to use a block to scope a declared function. For example:
as compared with:
as compared with:
Context is key
Most discussions about block scoping are specifically referencing the use of
let declarations within a block. This is the case where the block scope shines, out-of-the-box, without any stict mode: the ES6 declarations of
const, and even
class never leak outside of the scope.
Let’s see what happens when I rewrite
foo() as a constant function expression (CFE… can I coin that?):
However, keeping declared functions (using the
function keyword) contained within the block scope requires strict mode to be enabled, and that isn’t usually mentioned in guides that discuss using this technique.
Also of note:
var will never contain itself within a block scope, no matter what you try. It will always leak out, and while there’s not really any great reasons to use
var any longer, as you’ll see shortly, transpilers still do.
Transpiling with Babel and Traceur
If you’re writing client-side ES6 code, even with the high levels of compliance we’re starting to see today, you’re still likely to be transpiling down to ES5.
Oddly, block scoping in Babel is one of those rare cases where things do act differently.
A practical example would be taking some Angular 1.x code, and instead of wrapping a file with an IIFE, let’s wrap it with block scoping:
Run this through Babel (in ES2015/ES6 mode), and the result is:
Note that the function declaration is now a function expression, using
var leaks out of block scope by design (it is function scoped, not block scoped), and the whole point of wrapping the file in an IIFE is lost as the function now lives in global scope.
Running this through an alternative, Traceur, basically results in the same thing:
Why Babel and Traceur don’t just convert the block into an IIFE, I’m not sure. Certainly if you’ve declared you’d like the block handled strictly, I would think that is enough to infer that transpiling it to an IIFE.
For Babel, the only way to get a more correct result is to use the ES2016 setting, however that may be making too much of an assumption about the freshness of your users browsers.
In coming years, block scope is certain to replace the need for IIFEs. Until that time, however, be very careful about using them, and to use them effectively and without side-effects, don’t make assumptions – make good tests.
- For reference, a quick test you can run to reveal a function leaking from block scope: blockscope.js