导入
静态 import
声明用于导入只读实时 绑定,这些绑定由另一个模块 导出。导入的绑定被称为实时绑定,因为它们会由导出绑定的模块更新,但不能由导入模块重新赋值。
为了在源文件中使用 import
声明,该文件必须由运行时解释为一个 模块。在 HTML 中,这是通过在 <script>
标签中添加 type="module"
来实现的。模块会在 严格模式 下自动解释。
还有一种类似函数的动态 import()
,它不需要 type="module"
的脚本。
语法
import defaultExport from "module-name";
import * as name from "module-name";
import { export1 } from "module-name";
import { export1 as alias1 } from "module-name";
import { default as alias } from "module-name";
import { export1, export2 } from "module-name";
import { export1, export2 as alias2, /* … */ } from "module-name";
import { "string name" as alias } from "module-name";
import defaultExport, { export1, /* … */ } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";
defaultExport
-
将引用模块的默认导出的名称。必须是有效的 JavaScript 标识符。
module-name
-
要从中导入的模块。规范的评估由主机指定。这通常是包含模块的
.js
文件的相对或绝对 URL。在 Node 中,没有扩展名的导入通常是指node_modules
中的包。某些捆绑器可能允许导入没有扩展名的文件;检查您的环境。只允许单引号和双引号字符串。 name
-
将用作引用导入的命名空间的模块对象的名称。必须是有效的 JavaScript 标识符。
exportN
-
要导入的导出的名称。名称可以是标识符或字符串字面量,具体取决于
module-name
声明要导出的内容。如果它是字符串字面量,则必须将其别名为有效的标识符。 aliasN
-
将引用命名导入的名称。必须是有效的 JavaScript 标识符。
"module-name"
后面可以是一组以 with
关键字开头的 导入属性。
描述
import
声明只能出现在模块中,并且只能出现在顶层(即不在块、函数等内部)。如果在非模块上下文中遇到 import
声明(例如,没有 type="module"
的 <script>
标签、eval
、new Function
,它们都具有 "script" 或 "function body" 作为解析目标),则会抛出 SyntaxError
。要在非模块上下文中加载模块,请改用 动态导入 语法。
所有导入的绑定不能与任何其他声明处于相同的范围,包括 let
、const
、class
、function
、var
和 import
声明。
import
声明旨在从语法上严格(例如,只有字符串字面量规范,只允许在顶层,所有绑定必须是标识符),这使得模块可以在评估之前进行静态分析和链接。这是使模块本质上是异步的关键,为 顶层 await 等功能提供了支持。
导入声明的形式
import
声明有四种形式
- 命名导入:
import { export1, export2 } from "module-name";
- 默认导入:
import defaultExport from "module-name";
- 命名空间导入:
import * as name from "module-name";
- 副作用导入:
import "module-name";
以下是澄清语法的示例。
命名导入
假设从 my-module
模块隐式导出名为 myExport
的值,例如 export * from "another.js"
,或使用 export
语句显式导出,这会将 myExport
插入当前作用域。
import { myExport } from "/modules/my-module.js";
您可以从同一个模块导入多个名称。
import { foo, bar } from "/modules/my-module.js";
您可以在导入时重命名导出。例如,这会将 shortName
插入当前作用域。
import { reallyReallyLongModuleExportName as shortName } from "/modules/my-module.js";
模块还可以将成员导出为字符串字面量,它不是有效的标识符,在这种情况下,您必须将其别名才能在当前模块中使用它。
// /modules/my-module.js
const a = 1;
export { a as "a-b" };
import { "a-b" as a } from "/modules/my-module.js";
注意:import { x, y } from "mod"
不等同于 import defaultExport from "mod"
然后从 defaultExport
解构 x
和 y
。命名导入和默认导入是 JavaScript 模块中不同的语法。
默认导入
默认导出需要使用相应的默认导入语法导入。最简单的版本直接导入默认值
import myDefault from "/modules/my-module.js";
由于默认导出没有显式指定名称,因此您可以随意为标识符命名。
也可以使用命名空间导入或命名导入来指定默认导入。在这种情况下,必须先声明默认导入。例如
import myDefault, * as myModule from "/modules/my-module.js";
// myModule.default and myDefault point to the same binding
或
import myDefault, { foo, bar } from "/modules/my-module.js";
导入名为 default
的名称与默认导入具有相同的效果。由于 default
是保留字,因此有必要为其别名。
import { default as myDefault } from "/modules/my-module.js";
命名空间导入
以下代码将 myModule
插入当前作用域,其中包含位于 /modules/my-module.js
中的模块的所有导出。
import * as myModule from "/modules/my-module.js";
在这里,myModule
代表一个命名空间对象,其中包含所有导出作为属性。例如,如果上面导入的模块包含一个导出 doAllTheAmazingThings()
,则可以这样调用它
myModule.doAllTheAmazingThings();
myModule
是一个 密封 对象,具有 null
原型。默认导出可作为名为 default
的键使用。有关更多信息,请参阅 模块命名空间对象。
注意:JavaScript 没有像 import * from "module-name"
这样的通配符导入,因为存在命名冲突的可能性很高。
仅为其副作用导入模块
导入整个模块以仅获得副作用,而不导入任何内容。这会运行模块的全局代码,但实际上不会导入任何值。
import "/modules/my-module.js";
这通常用于 polyfill,它们会修改全局变量。
提升
导入声明被 提升。在这种情况下,这意味着导入引入的标识符在整个模块作用域中可用,并且它们的副作用在模块的其余代码运行之前产生。
myModule.doAllTheAmazingThings(); // myModule.doAllTheAmazingThings is imported by the next line
import * as myModule from "/modules/my-module.js";
示例
标准导入
在此示例中,我们创建一个可重用模块,该模块导出一个函数来获取给定范围内所有素数。
// getPrimes.js
/**
* Returns a list of prime numbers that are smaller than `max`.
*/
export function getPrimes(max) {
const isPrime = Array.from({ length: max }, () => true);
isPrime[0] = isPrime[1] = false;
isPrime[2] = true;
for (let i = 2; i * i < max; i++) {
if (isPrime[i]) {
for (let j = i ** 2; j < max; j += i) {
isPrime[j] = false;
}
}
}
return [...isPrime.entries()]
.filter(([, isPrime]) => isPrime)
.map(([number]) => number);
}
import { getPrimes } from "/modules/getPrimes.js";
console.log(getPrimes(10)); // [2, 3, 5, 7]
导入的值只能由导出者修改
正在导入的标识符是实时绑定,因为导出它的模块可以重新分配它,并且导入的值会发生变化。但是,导入它的模块不能重新分配它。尽管如此,任何持有导出对象的模块都可以修改该对象,并且所有其他导入相同值的模块都可以观察到修改后的值。
您也可以通过模块命名空间对象观察新值。
// my-module.js
export let myValue = 1;
setTimeout(() => {
myValue = 2;
}, 500);
// main.js
import { myValue } from "/modules/my-module.js";
import * as myModule from "/modules/my-module.js";
console.log(myValue); // 1
console.log(myModule.myValue); // 1
setTimeout(() => {
console.log(myValue); // 2; my-module has updated its value
console.log(myModule.myValue); // 2
myValue = 3; // TypeError: Assignment to constant variable.
// The importing module can only read the value but can't re-assign it.
}, 1000);
规范
规范 |
---|
ECMAScript 语言规范 # sec-imports |
浏览器兼容性
BCD 表格仅在浏览器中加载
另请参阅
出口
import()
import.meta
- 导入属性
- 在 blogs.windows.com 上预览 ES6 模块以及 ES2015、ES2016 及更高版本的功能 (2016)
- ES6 深入解析:模块 在 hacks.mozilla.org 上 (2015)
- ES 模块:卡通深度解析 在 hacks.mozilla.org 上 (2018)
- 探索 JS,第 16 章:模块 由 Axel Rauschmayer 博士编写
- 导出和导入 在 javascript.info 上