Ambiguity with default exports and live bindings?

/#!/JoePea joe at trusktr.io
Fri Jul 8 19:22:56 UTC 2016


Interesting. I think it is very intuitive to think that `export default A`
where `A` is a variable would be live. I personally find that live bindings
are very helpful when dealing with circular dependencies.

For example, suppose we have two modules A and B:

```js
// B.js
import A, {defineClassA} from `./A`

if (!A) defineClassA() // this fires, and triggers the error, see A.js.

export default
class B extends A { /* ... */ }
```

```js
// A.js
import someFunction from './other-module'
import B from './B'

let A = null
export default A

if (!A) defineClassA() // this doesn't fire, the error is triggered by B.js
first.

export
function defineClassA() {
  someFunction() // Error happens here, triggered from B.js

  A = class {
    method() {
      console.log('B constructor:', B)
    }
  }
}
```

I was erroneously expecting the export statements to create live bindings,
so that my `defineClassA` call inside of B would cause A to be defined in
case module B evaluates first. But as you can see, that doesn't work.

So, I'll simply now prefer `export {A as default}`, in which case the
modules become

```js
// B.js
import A, {defineClassA} from `./A`

if (!A) defineClassA() // this fires, and triggers the error, see A.js.

class B extends A { /* ... */ }

export {B as default}
```

```js
// A.js
import someFunction from './other-module'
import B from './B'

let A = null
export {A as default}

if (!A) defineClassA() // this doesn't fire, the error is triggered by B.js
first.

export
function defineClassA() {
  someFunction() // Error happens here, triggered from B.js

  A = class {
    method() {
      console.log('B constructor:', B)
    }
  }
}
```

Although I love ES6 modules, I don't think these live binding semantics are
clear or intuitive. Although probably too late and it won't happen, what if
export bindings were more explicit with a `live` keyword?

```js
export {
  live bar, // live binding of variable bar
  foo // not live, exports value.
}

export // not live
function foo() {}

export live // live
function foo() {}

export default live A // A is a live binding

export default A // exports value

// etc...
```

The reason I suggest this is because JS seems to prefer having meaningful
keywords to make intent clear (for example, we chose to have the `await`
keyword inside `async` functions, but we could have gone the Java route and
used no keywords at all to make it hard to immediately see what is async
and what isn't).







*/#!/*JoePea

On Wed, Jul 6, 2016 at 4:38 PM, Bergi <a.d.bergi at web.de> wrote:

> /#!/JoePea schrieb:
>
>> Is it true one of the following does not create a live binding?
>>
>
> Yes and no.
>
> ```js
>> let A = 123
>> export default A // not a live binding?
>> ```
>>
>
> Actually it does create a live binding - to the variable with the name
> "*default*" which you cannot assign. But yes, the binding `A` is not
> exported.
>
> ```js
>> let A = 123
>> export {A as default} // live binding?
>> ```
>>
>
> Yes.
>
> If so, this seems like large source for unexpected behavior when people
>> create modules. I can imagine people easily overlooking the difference and
>> expecting live bindings in both cases (this already happened to me).
>>
>
> I think people do in general not expect live bindings at all. For
> declarations and the purpose of initialisation order, yes, but not for
> mutable variables. You should export `const`s.
> See also
> http://stackoverflow.com/q/35223111/1048572?what-is-the-difference-between-importing-a-function-expression-or-a-function-declaration-from-a-ES6-module
> .
>
> Kind regards,
>  Bergi
>
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20160708/9bd2cd19/attachment.html>


More information about the es-discuss mailing list