JavaScript进阶(五)ES6模块化语法

本文最后更新于:8 个月前

归纳ES6 Module语法、以及export和import的用法
同时简单介绍了CommonJS的用法,因其在Node.js环境中使用较多,而且可以和ES6 Module混用,所以作为拓展,以示区分

JavaScript进阶(五)ES6模块化语法

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。

它自动使用严格模式。模块功能主要由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

一、export命令

export常见用法示例

1
2
3
4
5
var firstName = 'Michael';
var lastName = 'Jackson';
function getName(){return firstName + lastName}

export { firstName, lastName, getName };

1.1 as关键字重命名

使用 as关键字 可以在导出变量时,对其重命名

1
2
3
4
5
6
7
8
function v1() { ... }
function v2() { ... }

export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};

对同一个变量,可以定义多个不同的名字(变量名字本质上是对外的接口)

1.2 export需要注意的问题

第一,export必须与模块内部的变量建立一一对应关系。示例如下:

错误示例:

1
2
3
4
5
6
7
8
9
export 1	//报错!因为这样在其它模块中因为缺少接口,而没办法取到这个值。
//就比如export 1和export 2在一起,你怎么区分import的是1还是2

var m=1
export m //报错!因为export m被解释成了export 1
//解析:第一种写法直接输出1;第二种写法通过变量m,还是直接输出1。1只是一个值,而不是接口

function f(){}
export f //报错!

正确写法如下:

1
2
3
4
5
6
7
8
9
10
11
12
export var m=1	//这里export的是变量m,(个人理解)

var m=1
export {m}
//解析:以上两种写法,规定了对外的接口`m`。其他脚本可以通过这个接口,取到值1。
//它们的实质是,在接口名与模块内部变量之间,建立了一一对应的关系。
//(对于这个解析,我还不太理解)

export function f(){}

function f(){}
export {f}

第二,export命令必须声明在模块顶层,如果export处于块级作用域内,会报错

1
2
3
4
var obj = {
name:'timegogo'
}
export {obj}
1
2
3
4
5
6
7
var obj = {
name:'timegogo'
}
function f(){
export {obj}
}
f() //报错!

1.3 export default

使用export命令导出的模块,必须预先知道里面的变量名才能进行import(或者直接import *)。但是用户并不一定知道模块里面的变量名,为了解决这个麻烦,可以用export default命令。

一个模块只能有一个export default语句!

使用export default导出的变量,在其它模块中import导入时可以指定为任意名字(而无须知道变量原本的名字),同时不需要用大括号包裹

本质上,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。

1
2
3
4
5
6
//module.js
function add(x,y){return x+y}
export default add

//app.js
import fn from 'module.js'

实质上等于下面的代码

1
2
3
4
5
6
//module.js
function add(x,y){return x+y}
export {add as default}

//app.js
import {default as fn} from 'module.js'

正是因为export default命令其实只是输出一个叫做default的变量,所以export default后面不能跟变量声明语句

1
2
3
4
5
6
7
8
export var a=1		//正确
export default var a=1 //错误!!

var a=1
export default a //正确,这里实质上是把变量a的值赋给了default

export default 1 //正确,这里是把 1 赋给了default
export 1 //错误!!

export default命令可以和 export命令 一起使用export default最多可以用一次,而export命令可以用多次

1
2
3
4
5
6
7
//module.js
export default function(obj){}
export function each(){}
export {each as forEach} //同一个方法(变量),可以定义多个接口

//app.js
import _,{each,forEach} from 'module.js' //这里的_表示默认接口

二、import命令

使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。

import命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块profile.js对外接口的名称相同

1
2
3
4
5
6
7
//module.js
export var firstName='Q'
var lastName = 'A'
export {lastName}

//app.js
import {firstName,lastName} from 'module.js'

import导入默认接口时,可以不用大括号包裹,而且可以任意指定变量名

1
2
3
4
5
6
//module.js
function bar(){}
export default bar

//app.js
import fn from 'module.js'

2.1 as关键字重命名

使用 as关键字 可以在导入变量时,对其重命名

1
2
3
4
5
//module.js
export var lastName = 'A'

//app.js
import {lastName as surname} from 'module.js'

2.2 import导入的变量是只读的

import导入的变量本质上是输入接口,所以不允许在加载模块的脚本里改写接口。但是如果导入的是一个对象,那么修改对象内部的属性是可以的

1
2
3
4
5
6
7
8
9
10
11
//module.js
var obj = {
name:'timegogo'
}
export default obj

//app.js
import person from 'module.js'
person = {} //报错!!不可以修改导入的变量
person.name = 'QA' //修改对象内部的属性是允许的,因为它并没有改写接口。
//修改name的结果会同步到源模块中,其它模块会读取到修改后的值,这样容易引发潜藏的bug!

2.3 import不能使用表达式和变量

因为import是静态执行,所以不能使用表达式和变量。import会提升到整个模块的头部,在所有代码之前执行

1
2
3
4
5
6
7
8
9
10
import {'f'+'oo'} from 'module.js'		//报错!不能在import语句中使用表达式

var module = 'module.js'
import {fn} from module //报错!不能使用变量

if(x==1){
import {foo} from 'module1.js'
}else{
import {foo} from 'module2.js'
}

2.4 import整体导入

使用*可以表示导入模块内所有接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//module.js
function f1(){console.log(1)}
function f2(){console.log(2)}
export {f1,f2}

//app1.js
import * as Fn from 'module.js'
Fn.f1() //1
Fn.f2() //2
Fn.f1 = ()=>{} //错误!不能改写接口

//app2.js
import * from 'module.js'
f1() //不知道是否可行

2.5 import 和 模块代码的执行顺序

在使用import的脚本中,import最先被执行,不管import语法所处的位置如何。

使用import语句后,会执行对应模块中所有代码

1
2
3
4
5
6
7
8
9
10
11
//module.js
console.log(1)
export function fn(){}
console.log(2)

//app.js
console.log(3)
import {fn} from 'module.js'
console.log(4)

//输出结果:1 2 3 4

三、export+import组合使用

先来看一个场景:我们先是在脚本中导入了变量,然后又把这些变量原封不动地导出

1
2
3
//my_module.js
import {foo,bar} from 'module.js'
export {boo,bar}

上面的代码可以改写为:

1
export {foo,bar} from 'module.js'

这就是export和import的组合使用

3.1 重命名导出

1
export {foo as myFoo} from 'module.js'

3.2 整体导入导出

1
export * from 'module.js'

3.2 默认接口导出

1
2
3
4
5
6
7
8
//第一种,default导出为default,注意:要用大括号包裹!!
export {default} from 'module.js'

//第二种,default导出为具名接口
export {default as es6} from 'module.js'

//第三种,具名接口导出为default
export {es6 as default} from 'module.js'

四、拓展:CommonJS

CommonJS 是一个项目,其目标是为 JavaScript 在网页浏览器之外创建模块约定。创建这个项目的主要原因是当时缺乏普遍可接受形式的 JavaScript 脚本模块单元

CommonJS基于Node.js,离开Node.js环境无法使用!

我们现在大多使用的是 webpack 进行项目构建打包,因为现在前端开发环境都是在 Node 环境原因,而 npm 的包都是 CommonJS 规范的,所以 webpack 对ES6模块进行扩展 支持 CommonJS,并支持node的导入npm包的规范

4.1 导出方式

第一种,单个导出

1
2
3
4
5
6
module.exports.age = 1
module.exports.foo = function(){}

exports.a = 'hello' //不加module.前缀也可以
//node为每一个模块提供了一个exports对象,它指向module.exports(当两者冲突时,以后者为准)
//在对外输出时,可以在这个exports对象上添加变量

第二种,整体导出

1
2
3
4
5
6
7
8
9
10
11
module.exports = { 
age: 1,
a: 'hello',
foo:function(){}
}

//或者
var age=1
var a='hello'
function foo(){}
module.exports = {age,a,foo}

4.2 导入方式

1
2
3
4
5
6
const foo = require('module.js')
foo.fn()

//解构赋值
const {fn} = require('module.js')
fn()

参考文章

Module 的语法 - ECMAScript 6入门 (ruanyifeng.com)

前端科普系列-CommonJS:不是前端却革命了前端 - 知乎 (zhihu.com)


JavaScript进阶(五)ES6模块化语法
http://timegogo.top/2023/02/10/JavaScript/JavaScript进阶(五)ES6模块化语法/
作者
丘智聪
发布于
2023年2月10日
更新于
2023年7月16日
许可协议