简体   繁体   中英

Are import values disconnected from their export values still read-only?

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 ?

Import bindings are always read-only, see the abstract CreateImportBinding operation in the spec, step 5:

  1. 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 comment, 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:

  1. The live binding of the export in the source module.
  2. 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 complicated to understand and more complicated 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM