7

给定的是以下模块结构:

// 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?

如果我有一个表达式(export default bexport default 1)的默认导出,则相应的导入与此导出值断开连接。考虑到这一点,这样的导入是否仍然是只读的,也就是说,我可以重新分配a还是c

4

1 回答 1

8

导入绑定始终是只读的,请参阅CreateImportBinding规范中的抽​​象操作,第 5 步:

  1. 在envRec中为N创建一个不可变的间接绑定,将MN2引用为其目标绑定,并记录绑定已初始化。

(我的重点)

ModuleDeclarationInstantiation在处理模块的导入条目时使用该操作。

所以:

b++; // Does this throw an error as well?

是的,b是只读的。

c.prop = true; // I think mutations are always allowed, right?

如果导出的对象允许,可以。

c = {}; // but are reassignment allowed too?

不,c是只读的。


在评论中,您说过:

但是当不再有实时绑定时,将这些变量设为只读是没有意义的

记住它们不是变量是有用的,它们是绑定。虽然变量是一种绑定,但并非所有绑定都是变量(即使在 ES5 和更早版本中)。

关于它们在无关紧要时是只读的,请记住涉及两层绑定:

  1. 源模块中导出的实时绑定。
  2. 消费模块中导入的实时绑定。

为了在 #1 的值不会改变时使 #2 可写,导入机制必须知道这一点,但该信息不在模块的 exports中。导出只是给出导出绑定的名称。

此外,具有可变导入绑定和不可变导入绑定更难理解,也更难实现。(从样式的角度来看,重新分配导入的绑定也是令人困惑的。)

记住它们是实时绑定这一事实也很重要,这意味着导入的值可能会改变。认为:

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

在那种特殊情况下,是代码mod2.js导致了更改(通过调用incrementFoo),但不一定是这样。这可能是因为发生了一些与时间相关的事件mod1.js以使值发生变化,或者是其他模块调用的结果mod1.js等。

但是因为mod2.js's是对'sfoo的实时绑定,所以可以看到变化。mod1.jsfoomod2.js

于 2016-09-02T10:04:31.043 回答