17. Module
Module
模块功能主要由两个命令构成
export命令用于规定模块的对外接口
import命令用于输入其他模块提供的功能
注意:
ES6的模块自动采用严格模式,不管有没有在模块头部加上"use strict"
export与import命令可以出现在模块的任何位置,只要处于模块顶层作用域就可以,如果处于块级作用域内等就会报错
17.1 export
一个模块就是一个独立的文件,该文件内部的所有变量外部都无法获取。如果希望外部能够读取模块内部的某个变量,就必须使用export命令输出该变量
具体操作:
export实际上时导出一个接口,让外界能通过该接口访问内部的数据,所以export会将要导出的变量装在一个对象中,通过传递出这个对象来使用导出文件内部的变量,所以在导出的时候有格式要求
- 导出变量
//profile.js
//第一种写法
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
//第二种写法(推荐)
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};//因为实际上时导出对象,所以用{}包裹
/*
以下是错误方法
*/
// 报错
export 1;
// 报错
var m = 1;
export m;
/*
export不能直接导出一个数据或者一个变量,必须要用对象包装起来
*/
- 导出函数
export function multiply(x, y) {
return x * y;
};
//当然也可以通过传入对象一起传递
function multiply(x,y){
return x * y;
}
export {multiply}
通常情况下,export导出的变量就是本来的名字,如果想要修改导出后变量的名字,可以使用as关键字重命名
function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion }; //这样在外界使用时就使用as后面被重新修改后的名字
export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值
export var gender = 'boy'; setTimeout(function(){ gender = 'girl'; }, 1000); //1s后内部的值发生改变,外界引用的也会发生改变
17.2 import
使用export命令定义了模块的对外接口以后,其他 JS文件就可以通过import命令加载并使用这个模块
具体操作:
使用import命令导入文件中需要的变量时也需要通过一个对象导入,并且必须与被导入摸版的对外接口(变量名)的名称一致
// main.js import { firstName } from 'my_module'; import { lastName } from 'my_module'; import { year } from 'my_module'; //等同于 import {firstName, lastName, year} from './profile'; console.log(firstName,lastName,year);
注:
如果导入的是JS文件,后缀名可省略
如果想为输入的变量重新取一个名字,也是用as关键字将输入的变量重命名
import { lastName as surname } from './profile';
import命令具有提升效果,会让导入的变量提升到整个模块的头部首先执行(但是不推荐这样做)
multiply(10,20);
import { multiply } from 'my_module';
- 由于import是静态执行,所以不能使用表达式和变量这些只有在运行时才能得到结果的语法结构
// 报错
import { 'mult' + 'iply' } from 'my_module';
// 报错
let module = 'my_module';
import { multiply } from module;
// 报错
if (x === 1) {
import { multiply } from 'module1';
} else {
import { multiply } from 'module2';
}
import引入的模块可以进行整体加载
用星号可以指定一个对象,所有输出值都加载在这个对象上面,使用整体加载时一般都是与as关键词一起用**来方便操作
**注意:**模块整体加载所在的那个对象,不允许在运行时发生改变,如修改属性值等
import * as person from './profile';
// 不允许以下操作
person.firstName = "import";
pero.son.lastName = "export";
17.3 export default
使用import命令的时候,需要知道所要加载的变量名或函数名,否则无法加载。为了不用阅读文档就能加载模块,需要用export default命令,为模块指定默认输出
- 导出
// export-default.js
//可以直接用于导出一个匿名函数
export default function () {
console.log('foo');
}
//也可以导出一个非匿名函数
export default function foo() {
console.log('foo');
}
//或
function foo() {
console.log('foo');
}
export default foo;
/*
有名函数的函数名在模块外部是无效的,加载的时候,视同匿名函数加载
*/
- 导入
其他模块加载该模块时,import命令可以为该匿名函数指定任意名字
// import-default.js
import customName from './export-default';
customName(); // 'foo'
**注:**这时import命令后面不用通过对象的包装,而是可以直接写入
原理解释
因为一个模块只能有一个默认输出,所以export default命令只能使用一次。这正对应了import命令后面才不用加大括号被对象包装,因为只可能唯一对应export default命令
本质上,export default就是输出一个叫做default的变量或函数,然后系统外界为它取任意名字,所以:
// add.js function add(x, y) { return x * y; } export {add as default}; // 等同于 export default add;
//main.js import { default as add } from 'add'; // 等同于 import foo from 'add';
正是因为export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句
// 正确 export var a = 1; // 正确 var a = 1; export default a;//实际是变量的二次赋值 // 正确 export default 1; // 错误 export default var a = 1;
17.4 复合写法
如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起
export { foo, bar } from 'my_module';
// 等同于
import { foo, bar } from 'my_module';
export { foo, bar };
模块的接口改名和整体输出
// 接口改名
export { foo as myFoo } from 'my_module';
// 整体输出
export * from 'my_module';