import

静态 import 声明用于导入只读的活动 bindings,这是另一个模块的 exported。导入的绑定称为实时绑定,因为它们由导出绑定的模块更新,但不能由导入模块重新分配。

¥The static import declaration is used to import read-only live bindings which are exported by another module. The imported bindings are called live bindings because they are updated by the module that exported the binding, but cannot be re-assigned by the importing module.

为了在源文件中使用 import 声明,运行时必须将该文件解释为 module。在 HTML 中,这是通过将 type="module" 添加到 <script> 标记来完成的。模块在 严格模式 中自动解释。

¥In order to use the import declaration in a source file, the file must be interpreted by the runtime as a module. In HTML, this is done by adding type="module" to the <script> tag. Modules are automatically interpreted in strict mode.

还有一个类似函数的动态 import(),不需要 type="module" 的脚本。

¥There is also a function-like dynamic import(), which does not require scripts of type="module".

语法

¥Syntax

js
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 标识符。

描述

¥Description

import 声明只能出现在模块中,并且只能出现在顶层(即不能出现在块、函数等内部)。如果在非模块上下文中遇到 import 声明(例如,没有 type="module"evalnew Function<script> 标记,它们都以 "script" 或 "函数体" 作为解析目标),则会抛出 SyntaxError。要在非模块上下文中加载模块,请改用 动态导入 语法。

¥import declarations can only be present in modules, and only at the top-level (i.e. not inside blocks, functions, etc.). If an import declaration is encountered in non-module contexts (for example, <script> tags without type="module", eval, new Function, which all have "script" or "function body" as parsing goals), a SyntaxError is thrown. To load modules in non-module contexts, use the dynamic import syntax instead.

所有导入的绑定不能与任何其他声明位于同一范围内,包括 letconstclassfunctionvarimport 声明。

¥All imported bindings cannot be in the same scope as any other declaration, including let, const, class, function, var, and import declaration.

import 声明被设计为语法严格(例如,仅字符串文字说明符,仅允许在顶层,所有绑定必须是标识符),这允许在评估模块之前对模块进行静态分析和链接。这是使模块本质上异步的关键,为 顶层等待 等功能提供支持。

¥import declarations are designed to be syntactically rigid (for example, only string literal specifiers, only permitted at the top-level, all bindings must be identifiers), which allows modules to be statically analyzed and linked before getting evaluated. This is the key to making modules asynchronous by nature, powering features like top-level await.

导入报关表格

¥Forms of import declarations

import 声明有四种形式:

¥There are four forms of import declarations:

下面是阐明语法的示例。

¥Below are examples to clarify the syntax.

命名导入

¥Named import

给定一个名为 myExport 的值,该值已从模块 my-module 隐式导出为 export * from "another.js" 或显式使用 export 语句,这会将 myExport 插入到当前作用域中。

¥Given a value named myExport which has been exported from the module my-module either implicitly as export * from "another.js" or explicitly using the export statement, this inserts myExport into the current scope.

js
import { myExport } from "/modules/my-module.js";

你可以从同一模块导入多个名称。

¥You can import multiple names from the same module.

js
import { foo, bar } from "/modules/my-module.js";

你可以在导入时重命名导出。例如,这会将 shortName 插入到当前作用域中。

¥You can rename an export when importing it. For example, this inserts shortName into the current scope.

js
import { reallyReallyLongModuleExportName as shortName } from "/modules/my-module.js";

模块还可以将成员导出为字符串文字,这不是有效的标识符,在这种情况下,你必须为其指定别名才能在当前模块中使用它。

¥A module may also export a member as a string literal which is not a valid identifier, in which case you must alias it in order to use it in the current module.

js
// /modules/my-module.js
const a = 1;
export { a as "a-b" };
js
import { "a-b" as a } from "/modules/my-module.js";

注意:import { x, y } from "mod" 不等于 import defaultExport from "mod" 然后从 defaultExport 解构 xy。命名导入和默认导入是 JavaScript 模块中不同的语法。

¥Note: import { x, y } from "mod" is not equivalent to import defaultExport from "mod" and then destructuring x and y from defaultExport. Named and default imports are distinct syntaxes in JavaScript modules.

默认导入

¥Default import

默认导出需要使用相应的默认导入语法进行导入。最简单的版本直接导入默认的:

¥Default exports need to be imported with the corresponding default import syntax. The simplest version directly imports the default:

js
import myDefault from "/modules/my-module.js";

由于默认导出没有显式指定名称,因此你可以为标识符指定任何你喜欢的名称。

¥Since the default export doesn't explicitly specify a name, you can give the identifier any name you like.

还可以使用命名空间导入或命名导入来指定默认导入。在这种情况下,必须首先声明默认导入。例如:

¥It is also possible to specify a default import with namespace imports or named imports. In such cases, the default import will have to be declared first. For instance:

js
import myDefault, * as myModule from "/modules/my-module.js";
// myModule.default and myDefault point to the same binding

or

js
import myDefault, { foo, bar } from "/modules/my-module.js";

导入名为 default 的名称与默认导入具有相同的效果。由于 default 是保留字,因此需要为该名称起别名。

¥Importing a name called default has the same effect as a default import. It is necessary to alias the name because default is a reserved word.

js
import { default as myDefault } from "/modules/my-module.js";

命名空间导入

¥Namespace import

以下代码将 myModule 插入到当前作用域中,其中包含来自位于 /modules/my-module.js 的模块的所有导出。

¥The following code inserts myModule into the current scope, containing all the exports from the module located at /modules/my-module.js.

js
import * as myModule from "/modules/my-module.js";

这里,myModule 代表一个命名空间对象,其中包含所有导出作为属性。例如,如果上面导入的模块包含导出 doAllTheAmazingThings(),你可以这样调用它:

¥Here, myModule represents a namespace object which contains all exports as properties. For example, if the module imported above includes an export doAllTheAmazingThings(), you would call it like this:

js
myModule.doAllTheAmazingThings();

myModule 是带有 null 原型机sealed 对象。默认导出可作为名为 default 的键使用。欲了解更多信息,请参阅 模块命名空间对象

¥myModule is a sealed object with null prototype. The default export available as a key called default. For more information, see module namespace object.

注意:JavaScript 没有像 import * from "module-name" 这样的通配符导入,因为名称冲突的可能性很高。

¥Note: JavaScript does not have wildcard imports like import * from "module-name", because of the high possibility of name conflicts.

导入模块仅用于其副作用

¥Import a module for its side effects only

导入整个模块只是为了产生副作用,而不导入任何内容。这会运行模块的全局代码,但实际上并不导入任何值。

¥Import an entire module for side effects only, without importing anything. This runs the module's global code, but doesn't actually import any values.

js
import "/modules/my-module.js";

这通常用于 polyfills,它会改变全局变量。

¥This is often used for polyfills, which mutate the global variables.

提升

¥Hoisting

导入报关单为 hoisted。在这种情况下,这意味着导入引入的标识符在整个模块范围内可用,并且它们的副作用是在模块的其余代码运行之前产生的。

¥Import declarations are hoisted. In this case, that means that the identifiers the imports introduce are available in the entire module scope, and their side effects are produced before the rest of the module's code runs.

js
myModule.doAllTheAmazingThings(); // myModule.doAllTheAmazingThings is imported by the next line

import * as myModule from "/modules/my-module.js";

示例

¥Examples

标准导入

¥Standard Import

在此示例中,我们创建一个可重用模块,该模块导出一个函数以获取给定范围内的所有素数。

¥In this example, we create a re-usable module that exports a function to get all primes within a given range.

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);
}
js
import { getPrimes } from "/modules/getPrimes.js";

console.log(getPrimes(10)); // [2, 3, 5, 7]

导入的值只能由导出者修改

¥Imported values can only be modified by the exporter

导入的标识符是实时绑定,因为导出它的模块可能会重新分配它,并且导入的值会改变。但是,导入它的模块无法重新分配它。尽管如此,任何持有导出对象的模块都可以改变该对象,并且导入相同值的所有其他模块都可以观察到改变的值。

¥The identifier being imported is a live binding, because the module exporting it may re-assign it and the imported value would change. However, the module importing it cannot re-assign it. Still, any module holding an exported object can mutate the object, and the mutated value can be observed by all other modules importing the same value.

你还可以通过 模块命名空间对象 观察新值。

¥You can also observe the new value through the module namespace object.

js
// my-module.js
export let myValue = 1;
setTimeout(() => {
  myValue = 2;
}, 500);
js
// 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);

规范

Specification
ECMAScript Language Specification
# sec-imports

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看

¥See also