当命名的 ES6 导出被异步改变时,为什么导入器会接收到变化?

When named ES6 exports are asynchronously mutated, why is the change picked up by importers?

考虑以下示例:

// module.mjs

export let member = "initial"

setTimeout(() => { 
  member = "mutated" 
}, 2000)
// index.mjs

import { member } from "./module.mjs"

setInterval(() => { 
  console.log(member) 
}, 300);

如果你 运行 index.mjs 它会打印 "initial" 几次并在 2 秒超时后开始打印 "mutated".

这让我很惊讶。我预计它会继续打印 "initial",因为对 member 的引用已更改。如果我的 index.mjs 看起来像这样

,我不会感到惊讶
// index.mjs

import * as allExports from "./module.mjs"

setInterval(() => { 
  console.log(allExports.member) 
}, 300);

allExports 的引用没有改变。因此,通过以 属性 的形式访问 member,我可以访问新的引用。公平地说,这会产生相同的结果。

但为什么它在第一种情况下也有效?每次构造函数调用的上下文时,导入引用都会更新吗?好吧,那么它也应该与默认导出一起使用。但是,在这样调整代码之后:

// module.mjs

let member = "initial"

export default member

setTimeout(() => { 
  member = "mutated" 
}, 2000)
// index.mjs

import member from "./module.mjs"

setInterval(() => { 
  console.log(member) 
}, 300);
根据我的直觉,

index.mjs 一直在打印 "initial"。嗯,也许默认导出和命名导出之间存在根本区别。如果我这样做会怎样:

// module.mjs

export let member = "initial"

setTimeout(() => { 
  member = "mutated" 
}, 2000)
// index.mjs

import * as allExports from "./module.mjs"

let { member } = allExports

setInterval(() => { 
  console.log(member) 
}, 300);

哇,它一直在打印 "initial"。这是我真正感到困惑的地方。我一直认为

import { member } from "./module.mjs"

只是将所有named-exports导入为对象然后进行解构的缩写。

所以我的问题是:named-exports 这种独特行为的基本原理是什么?

这是因为从技术上讲,ES6 模块导出和导入绑定而不是值。当你这样做时:

export let test = 0

您需要考虑导出的是 test 而不是 0。这意味着对变量的任何更改都将反映在它们的导入中。

一直打印的原因 initial:

// index.mjs

import * as allExports from "./module.mjs"

let { member } = allExports

setInterval(() => { 
  console.log(member) 
}, 300);

是因为allExportsis a special object。您正在此处创建一个新绑定并且值被复制,但您正在丢失导入的绑定。