Given is the following module structure:
// module A:
export let a = 1; // named export
export function inc() { a++; } // named export
// module B:
let b = 1;
export default b; // default export (equivalent to `export default 1`)
export function inc() { b++; } // named export
// module C:
let c = {};
export default c; // default export
// module E:
import a, {inc as incA} from "./A";
import b, {inc as incB} from "./B";
import c from "./C";
incA();
console.log(a); // logs 2, because "a" has a live connection to the export value
a++; // Error (because a is a live read-only view on the export)
incB();
console.log(b); // logs 1, because "b" is disconnected from the export value
b++; // Does this throw an error as well?
c.prop = true; // I think mutations are always allowed, right?
c = {}; // but are reassignment allowed too?
If I have a default export of an expression (export default b
or export default 1
) a corresponding import is disconnected from this export value. Considering this, is such an import still read-only that is, can I reassign a
or c
?
Given is the following module structure:
// module A:
export let a = 1; // named export
export function inc() { a++; } // named export
// module B:
let b = 1;
export default b; // default export (equivalent to `export default 1`)
export function inc() { b++; } // named export
// module C:
let c = {};
export default c; // default export
// module E:
import a, {inc as incA} from "./A";
import b, {inc as incB} from "./B";
import c from "./C";
incA();
console.log(a); // logs 2, because "a" has a live connection to the export value
a++; // Error (because a is a live read-only view on the export)
incB();
console.log(b); // logs 1, because "b" is disconnected from the export value
b++; // Does this throw an error as well?
c.prop = true; // I think mutations are always allowed, right?
c = {}; // but are reassignment allowed too?
If I have a default export of an expression (export default b
or export default 1
) a corresponding import is disconnected from this export value. Considering this, is such an import still read-only that is, can I reassign a
or c
?
- 1 Can't you just run the code and see the results? – jcubic Commented Sep 2, 2016 at 9:54
-
You can try to run the code with
node --harmony
– jcubic Commented Sep 2, 2016 at 14:46
1 Answer
Reset to default 8Import bindings are always read-only, see the abstract CreateImportBinding
operation in the spec, step 5:
- Create an immutable indirect binding in envRec for N that references M and N2 as its target binding and record that the binding is initialized.
(my emphasis)
That operation is used by ModuleDeclarationInstantiation
when processing the import entries of the module.
So:
b++; // Does this throw an error as well?
Yes, b
is read-only.
c.prop = true; // I think mutations are always allowed, right?
Provided the exported object allows it, yes.
c = {}; // but are reassignment allowed too?
No, c
is read-only.
In a ment, you've said:
but when there isn't a live-binding anymore, it makes no sense to make such variables read-only
It's useful to remember that they aren't variables, they're bindings. While variables are one kind of binding, not all bindings are variables (even in ES5 and earlier).
Regarding them being read-only when it doesn't matter, remember that there are two layers of bindings involved:
- The live binding of the export in the source module.
- The live binding of the import in the consuming module.
In order to make #2 writable when the value from #1 isn't going to change, the import mechanism would have to know that, but that information isn't in the module's exports. The exports just give the name of the exported binding.
Moreover, having mutable import bindings as well as immutable import bindings is more plicated to understand and more plicated to implement. (Reassigning imported bindings is also, from a style perspective, confusing.)
It's also important to remember that the fact they're live bindings means the value of an import may change. Assume:
mod1.js
:
export let foo = 41;
export function incrementFoo() {
++foo;
};
mod2.js
:
import { foo, incrementFoo } from "./mod1.js";
console.log(foo); // 41
incrementFoo();
console.log(foo); // 42 <== it changed
In that particular case, it was code in mod2.js
that caused the change (by calling incrementFoo
), but it doesn't have to be. It could be because some time-related event happened in mod1.js
to make the value change, or the result of a call by some other module into mod1.js
, etc.
But because mod2.js
's foo
is a live binding to mod1.js
's foo
, mod2.js
sees the change.