15:38
<nicolo-ribaudo>

Hello, maybe someone here knows the history of block-scoped functions.

We have a very old Babel plugin, enabled by default, that does this transform (@babel/plugin-transform-block-scoped-functions):

{ function f() {} }
// --->
{ let f = function f() {} }

This plugin was created in response to this issue: https://github.com/babel/babel/issues/514

Does anyone know:

  • why does Babel need to transform functions in blocks? Do they not work in older engines or older ECMAScript versions?
  • should the transform use let or var?
15:47
<littledan>
the transform should use let because the semantics of JS of functions in a block is that they're lexically scoped to the block (modulo Annex B 3.3)
15:48
<littledan>
The history is that, prior to ES6, in sloppy mode, each engine had its own way of hoisting functions out of blocks. This transform prevents that non-standard behavior.
15:49
<littledan>
it sounds like the transform implements the strict mode semantics. Sloppy mode semantics, in Annex B 3.3, is a mess and I don't recommend looking at it if you want to maintain your sanity :)
15:49
<littledan>
I think it's not necessary to enable this plugin by default since it is only needed if you target a pretty old JS engine
15:51
<nicolo-ribaudo>

Thank you! Follow-up question:

We also have this test, that ensures that the function is hoisted to the outer scope when there is a variable with the same name (but only when we are also compiling let/const to var):

var run = function () {
  return false;
};

if (true) {
  function run() {
    return true;
  }
}

function test() {
  return run();
}

// ---- OUTPUT --->

var run = function () {
  return false;
};
if (true) {
  var run = function () {
    return true;
  };
}
function test() {
  return run();
}

is this annex b behavior?

15:52
<nicolo-ribaudo>
I think it's not necessary to enable this plugin by default since it is only needed if you target a pretty old JS engine
Yeah right now we compile down to the oldest targets we support; this is going to change in Babel 8
15:55
<littledan>

Thank you! Follow-up question:

We also have this test, that ensures that the function is hoisted to the outer scope when there is a variable with the same name (but only when we are also compiling let/const to var):

var run = function () {
  return false;
};

if (true) {
  function run() {
    return true;
  }
}

function test() {
  return run();
}

// ---- OUTPUT --->

var run = function () {
  return false;
};
if (true) {
  var run = function () {
    return true;
  };
}
function test() {
  return run();
}

is this annex b behavior?

reviewing https://tc39.es/ecma262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics (actually B 3.2 now), yeah, it looks like an outer var declaration does not block this hoisting, though a let declaration would
15:56
<nicolo-ribaudo>

Thank you!

Another thing we should to in Babel 8 is deciding if we want to consider Annex B or not, instead of enabling it sometimes

15:59
<littledan>
IMO it should be enabled always in Babel, given that Babel tends to target web/Node environments, which always enable Annex B
16:00
<nicolo-ribaudo>
And whichever choice we make, there will be a plugin to do the opposite
16:01
<littledan>
ah so it is an even lower pressure decision than I was imagining