支撑 ES6 模块的两个主要关键字是 import 和 export 。它们在语法上有着自己的特点,这篇文章主要介绍这些特点。
首先值得注意的一点就是这两个关键字都必须在最顶层作用域使用。也就是说,它们必须出现在所有代码块和函数的外面。
一. 导出成员
export 关键字可以放在声明前面,或者作为一个操作符与要导出的列表绑定。例:
//放在声明前面
export function foo(){ //... };
export var a = [1,2,3];
// 与导出列表绑定
function b(){ // ... };
var c = [1,2,3];
var d = 'd';
export { b,c,d };
以上都称为命名导出,因为导出的变量和函数与名称绑定。
注意点:
- 没有用 export 标识的模块作用域内的内容都是私有的。
- 这里说的顶层作用域是模块本身的作用域,不是全局。模块虽然还能够访问“全局”作用域,但是“全局”作用域不作为词法上的顶层作用域。
---------------------------分割线-------------------------------
function item(){ .. }
export { item as newItem };
var year = 2019;
export { year };
// 导出 year 之后再对它进行操作
year = 2020;
上面这个例子:
- 如果导入 item ,只有 newItem 可以导入,item 是隐藏在模块内部的。
- 导出 year 之后再对它赋值,year 的值是 2020。
产生这种现象的本质是绑定(export 导出变量)指向变量本身的引用或者指针,而不是这个值的复制。
引用的情况是针对导出简单数据类型来说的,导出指向的不是那个值,而是那个值的引用;指针这个概念JavaScript中并没有,说这个的意思是,导出复杂数据类型的时候,导出指向的是指向堆的地址,不是堆里面的内容。
默认导出的细节,例:
function foo(){...}
export default foo;
function boo(){...}
export {boo as default};
注意点:
- export default foo 导出的是函数表达式绑定,也就是说相当于 export default function foo(…),如果之后给 foo 赋予一个不同的值,从此模块导入 foo 的话,仍然是原来的函数,不是新的值。
- 而 boo 导出,绑定到的是 boo 标识符而不是它的值,与之前说的绑定是一样的。
所以,如果你默认导出的值需要更新,采用 export {boo as default};如果不需要更新,采用谁都可以。
二. 导入成员
如果想导入一个模块 api 的某个特定的命名成员到顶层作用域,可以使用以下语法:
import { foo, boo } from 'file';
{ foo, boo } 这个语法看起来像是对象字面量和对象解构的语法。但实际上不是,它就是专用于模块的一种形式而已。
列出的标识符 foo,boo 必须和模块 api 的命名导出匹配。且它们会在当前作用域绑定为顶层标识符。
可以对导入绑定标识符重命名,例:
import {foo as redFoo } from 'file';
redFoo();
如果是只有一个默认导出,可以省略 {…} 语法,例:
function foo(){...}
export default foo;
import foo from 'file';
// 或者
import { default as foo} from 'file';
以上都是单独导入的,如果想要把一个模块的api全部导入到一个命名空间,有一种方式,也称为命名空间导入。
export function foo(){...}
export var x = 42;
export function boo(){...}
import * as container from 'file';
container.foo();
container.x; // 42
上面的例子中,导出全部被绑定到 container 这个命名空间。
还有一点,import 导入是提升的。例:
foo();
import foo from 'file';
foo()可以正常执行,说明 foo 被提升到了顶层作用域最上面。
import 'file'
上面这个例子,一般来说没什么作用,它相当于加载了一遍‘file’模块(如果它还没有加载过)。这可以用来执行一些有副作用的模块。
Kedar 发布了45 篇原创文章 · 获赞 3 · 访问量 607 私信 关注