ECMAScript®2015 语言规范中添加了模块化的功能。而 JavaScript 模块化最依赖的两个语法就是 export
关键字和 import
关键字
- 前者用于导出一个模块中声明的提供给其它模块使用的东西
- 后者则用于将一个模块中使用
export
导出的东西导入到当前所在的脚本中
本章节,我们就来讲讲前者,也就是 export
关键字
export 关键字说明
MDN 上对 export
的描述是
export 语句用于在创建 JavaScript
模块时,从模块中导出函数、对象或原始值,以便其他程序可以通过 import 语句使用它们
理解这句话就要弄懂两个地方,一个是什么是 JavaScript 模块,另一个是可以导出的东西到底有那些 ?
JavaScript 模块
对于什么是 JavaScript
模块 ? 我想,这应该不是一个问题,但是,它其实又是一个问题
JavaScript 模块,通俗的江,就是一个独立的 JavaScript
文件,模块中可以包含一些变量,一些常量,一些类,一些函数,甚至是任何可以执行的代码。
模块 vs 普通脚本文件
创建模块时,一个 JavaScript 模块,跟我们平时写的 JavaScript 文件没什么差别
那么 JavaScript 模块和我们平时编写的普通的 JavaScript 文件有啥区别呢 ?
区别就在于运行时的代理 ( 浏览器或 Node.js ) 是如何对待两者的
-
对于普通文件,准确的说,是浏览器中的普通脚本文件 ( 因为 Node.js 下不存在普通文件 ),所有的文件级别的声明都是默认全局的,也就是都在
window
这个超级变量上 -
而对于模块,例如
Node.js
中的文件,或者浏览器上声明为text/module
的文件,文件级别的声明默认是私有的,除非特别说明 ( 使用export
导出 ),否则外部的其它文件是无法访问的
如果要细细说明,那么就会涉及到 export
和 import
两个关键字语法,就会产生鸡和蛋的问题。
所以,我们这里,对模块只有一个定义,就是一个 JavaScript 文件
export 语法
ECMAScript®2015 对 export
的语法做了如下定义
export { name1, name2, …, nameN }; export let name1, name2, …, nameN; // also var export let name1 = …, name2 = …, …, nameN; // also var, const export function FunctionName() {...} export class ClassName {...} export default expression; export default function (…) { … } // also class, function* export default function name1(…) { … } // also class, function* export { name1, name2, …, nameN } from …; export * from …; export { name1 as default, … }; export { variable1 as name1, variable2 as name2, …, nameN }; export { import1 as name1, import2 as name2, …, nameN } from …;
参数 nameN
表示要导出的标识符(用来被其他脚本的 import
导入 )
当看到这些语法时是不是吓了一大跳,这么复杂...其实,你只要阅读了我的说明,就不会觉得复杂了
你也看到了,我其实是故意将这些语法分为 5
组的,每各分组代表了一种用途
-
第一组:导出一个模块中声明的东西
大家先想想,对于一个网页中的普通的文件,我们在文件中定义或声明的什么东西才会添加到
window
超全局变量上 ?想想 ?
- 使用
var
声明的变量 - 使用
let
声明的不可变变量 - 使用
const
声明的常量 - 使用
function
声明的函数 - 使用
function*
声明的生成器函数 6.. 使用class
声明的类
所以,对于一个 JavaScript 文件,所有能被添加到
window
超全局变量上的东西,都能被导出而第一个
export { name1, name2, …, nameN };
与后面几个的差别,就相当于是前者先声明这些东西,然后再导出js var name1; const name2; let name3; export name1, name2, …, nameN;
后者将声明和导出放到一起
- 使用
-
第二组:第二组与第一组的差别,就在于
default
关键字和export default expression
-
default
是默认的意思,export default
的意思就是默认导出,也称之为匿名导出,说默认导出你可能不太理解,说匿名导出你应该就理解了,因为JavaScript
有匿名函数的存在hello.js
var hello = function(){} export default hello;
当使用
export default hello
的时候,对于导入语法import func from hello
,func
其实就是hello
所以,JavaScript 模块有一条规则,那就是 只能有一个
export default
JavaScript 模块还有另一个规则,就是 **不能使用
var
,let
或const
作为默认导出- 理解了
export default
那么对于export default expression
应该就很好理解了,就是先求取expression
表达式的值,然后再export default
-
-
export ... from ...
对于前面的两组,都是导出export
关键字所在的模块中的声明,但export ... from ...
则可以导出另一个模块中声明的东西这个语法很有意思,有了这种语法,我们就可以创建子模块功能,然后在同一入口中导出所有公开的声明
假设我们有一个模块
greeting
,目录结构如下. |-- index.js |--- hello.js
假设
hello.js
中的代码如下export function hello() { return 'Hello World'; }
那么
index.js
中就可以使用export hello from hello
对于其它需要使用
greeting
模块的项目,只要使用下面的语句就可以使用hello
函数import hello from index
-
export * from …;
,当你看懂了第三组语法,那么第四种语法就很简单了,就是一种偷懒的写法,就是把...
文件中导出的声明再次导出,本质上来说,就是export name1,name2,name3,... from ...
的简写需要注意的是:
export * from ...
不会导出已导入模块中的默认导出,如果要导出已导入模块中的默认导出,需要使用下面的语句import mod from "mod"; export default mod;
-
export as
语法,我们也称之为别名导出,原来export name1
的时候导出的名字是name1
,那么使用export name1 as alias1
的时候,就是给name1
取一个别名alias1
然后再导出这个别名
这 5 组导出方式,我们可以根据有无 default
关键字分类,命名导出
和 默认导出
,这种划分的原因一方面是因为有无 default
关键字,另一方原因是 import
也会区别对待它们
-
命名导出对导出多个值很有用。在使用
import
导入期间,必须使用相应对象的相同名称 -
可以使用任何名称导入默认导出,唯一的规则就是 只能有一个默认的导出 ,例如
假设同一个目录下存在两个文件
test.js
和main.js
test.js
中的内容如下export default k = 12; // in file test.js
main.js
中的内容如下import m from './test' // 我们可以自由使用任意标志符来替换 k,比如 m,因为 k 是默认导出的 console.log(m); // will log 12
范例
-
使用命名导出
假设在某个模块
mymodule.js
中有如下代码mymodule.js
function cube(x) { return x * x * x; } const foo = Math.PI + Math.SQRT2; export { cube,foo };
那么再其它脚本中,我们可以这样使用
import { cube, foo } from 'mymodule.js'; console.log(cube(3)); // 27 console.log(foo); // 4.555806215962888
-
默认导出
如果我们要导出一个值或模块中的返回值,就可以使用默认导出
mymodule.js
export default function cube(x) { return x * x * x; }
然后,在另一个脚本中,可以直接导入默认导出:
import cube from 'mymodule'; console.log(cube(3)); // 27
浏览器兼容性
虽然 export
是 ES2015
标准规范中的东西,但很遗憾,戒指目前,在很多浏览器中仍然不被支持
此特性目前仅在 Safari
和 Chrome
原生实现,当然也为此除了很多转换器,比如 Traceur Compiler,Babel 或 Rollup
其中以 Babel 最为出名
至于具体的浏览器兼容性,可以查看下表
PC 浏览器
至于浏览器兼容性问题,可以访问
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/export