ES6 - 基础学习(19): 模块化 导出与导入

概述

在 ES6前, 实现模块化使用的是 RequireJS 或 seaJS(分别是基于 AMD规范的模块化库,和基于 CMD规范的模块化库)。ES6则引入了模块化,其设计思想是尽量的静态化,使得在编译时就能确定模块的依赖关系,以及输出和输入的变量。

ES6的模块化分为导出(export) 与 导入(import)两个模块。通过 export命令显式指定导出的代码,再通过 import命令导入。

特点

  1、ES6的模块自动开启严格模式,不管模块头部是否有 “use strict” 字段。

  2、模块中可以导出 和 导入各种类型的变量,如函数,对象,字符串,数字,布尔值,类等。

  3、每个模块都有自己的上下文,每一个模块内声明的变量都是局部变量,不会污染全局作用域。

  4、每一个模块只加载一次(是单例的), 若再去加载同目录下同文件,系统会直接从内存中读取。

export 与 import 的基本用法

export命令:模块化变量,对象、函数等,规定模块的对外接口。

// index_export.js
// 一个模块就是一个独立的文件。该文件内的所有变量,对象和方法,外部都无法获取也无法使用。如果想使用该模块的变量、对象和方法,就需要用到 export关键字,将这些内容输出。
// 分别导出
export var year = 2020;
export var month = '03';
export var day = 12;
export var time = "21:34";

// 集体导出
var year = 2020;
var month = '03';
var day = 12;
var time = "21:34";

var testFunc = function () {
    console.log('index_export');
};

export {year, month, day, time,testFunc};    // 在 export命令后面,用大括号显示指定要输出的变量集合。与上一种写法(直接放在 var语句前)功能是一样的,但这种方式更优越。
// 因为将 export命令放在脚本尾部,一眼就能看清输出了哪些变量,那些方法,清晰明了。

  1、export命令规定的是对外的接口,因此接口名必须与模块内部的变量名一一对应,排列顺序不需要一致,随便排都行。

// index_export.js
// 以下是导出变量的三种写法,其他模块导入 index_export.js后就可以通过这个接口,取得变量 "year"了
export var year = 1992;

var year = 1992;
export {year};

var year = 1992;
export {year as year_1992};

// 导出方法跟变量一样
export function testFunc() {
    console.log('index_export');
}

function testFunc() {
    console.log('index_export');
}
export {testFunc};

  2、export命令可以出现在模块的任何位置,但必须处于模块顶层。如果命令处于块级作用域内,就会报错,因为处于条件代码块内时,就无法做静态优化了,违背了 ES6模块设计的初衷,下面的 import命令也是如此。

var year = 1992;
function testFunc() {
    export default 'year'         // Uncaught SyntaxError: Unexpected token 'export'
}
testFunc();

  3、若不同模块导出的接口名称 变量名命名出现重复, 则可以使用 as重新命名变量名,同样下面的 import命令也是如此。

import命令:导入其他模块提供的功能。

使用 export命令定义了模块的对外接口之后,其他 模块(JS文件)就可以通过 import命令加载这个模块,然后使用该模块内定义的变量、对象和方法。

只读属性:不允许在加载模块的脚本里面,改写接口的引用指向,即可以改写 import变量类型为对象的属性值,但不能改写 import变量类型为基本类型的值。

// index_export.js
var year = 2020;
var month = '03';
var day = 12;
var time = {
    hour:21,
    minute:34,
    second:56
};

var testFunc = function () {
    document.getElementById("index_import").innerHTML = 'index_export';
};

export {year, month, day, time, testFunc};
// index_import.js
// import命令,用于加载index_export.js文件,并从中导入变量、对象、方法等。import命令通过一对大括号导入其他模块输出的变量、对象、方法等,且大括号内的变量名,必须与被导入模块(index_export.js)对外接口的名称相同。
import {year, month, day, time, testFunc} from './index_export.js';
testFunc();

// import命令输入的变量原则上只读的,import命令本质是输入接口。
// 1、不允许在加载模块的脚本内,改写或赋值 被导入模块内的接口和常量。
year = 2020;                          // Uncaught TypeError: Assignment to constant variable.    year已经被分配给一个常数,不允许修改。

// 2、允许在加载模块的脚本内,重新赋值 被导入模块内的 对象的属性。
// 这种方式虽然修改了被导入模块内的数据,但会导致其他模块(导入了该模块的模块)读取该值时也发生改变,且不易查错。所以建议凡是输入变量,能不动最好不动,免得遗患无穷。
time.hour = 11;
console.log(time.hour);                // 11

1、import命令会将导入的内容提升到整个模块的头部,首先执行。 import命令是在静态解析阶段执行,所以它是一个模块之中最早执行的,因此命令中不能使用表达式和变量,这些语法结构只有在代码运行时才会得到结果。

// 下面两种导入方式都是错误的,因为 import命令中用到了表达式、变量以及if结构,在静态分析阶段,这些语法都没有值,也就没法执行并取得结果。
import {year, month,100 + 1} from 'index_export.js';            // Uncaught SyntaxError: Unexpected number


if (true) {                                                    // Unexpected token '{'
    var tempStr = 'tempStr';
    import { year, tempStr + '123' } from 'index_export.js';
} else {
    import { year } from 'index_export.js';
}

2、如果重复执行同一条 import命令,则系统只会执行一次,而不会多次执行。import同一模块,声明不同接口引用,会声明对应变量,但只执行一次import 。

// index_import.js
import {year} from 'index_export.js';
import {time} from 'index_export.js';

// 等同于
import {year, time} from 'index_export.js';        // 虽然 year和time 分别在两个 import命令中加载,但它们出自同一个index_export实例。因而 import语句合被并执行,即单例模式(Singleton模式)。

 模块的整体加载

模块的整体加载,即用星号(*)指定一个对象,然后把所有需要输出的变量都加载在这个对象上。

// index_export.js
let year = 2020;
let month = '03';
let day = 12;

let testFunc = function () {
    console.log(`${year}-${month}-${day}`);
    document.getElementById('index_import').innerHTML = `${year}-${month}-${day}`;
};

export {year, month, day, testFunc};
// index_import.js
import * as time from './index_export.js';

console.log(`年-月-日:${time.year}-${time.month}-${time.day}`);
time.testFunc();

  

export default 命令

前面使用 import命令,在 模块导入时需要清楚的知道所要加载的变量、对象、方法...... 它们对应的 变量名、对象名、方法名......,否则无法进行加载。但实际情况是,我既想快速上手,而又不想花时间去看文档,去了解模块内的属性和方法,那咋办呢。于是就催生了 export default命令,为模块指定默认导出。

  1、通过 export方式导出,在导入时要加大括号({ }),export default则不需要。

  2、export default向外暴露的成员,可以使用任意变量来接收。

// index_export_default.js
// 1、模块文件index_export_default.js,默认输出一个匿名函数,其他模块在加载该模块时,import命令可以为该匿名函数定义任意名称。
export default function () {
    console.log(`index_export_default`);
}
// index_export_default.js
// 2、export default命令也可以输出非匿名函数。
// testFunc函数的函数名是testFunc,但在 index_export_default模块外部这些函数的函数名是无效的,加载时,视同匿名函数加载。
export default function testFunc() {
    console.log(`index_export_default`);
}

//
function testFunc() {
    console.log(`index_export_default`);
}
export default testFunc;
// index_import.js
// 其他模块在加载 export default命令默认输出的模块时,import命令可以为该模块定义任意名称。
import exportDefault from './index_export_default.js';        // 此时 import命令不再需要知道原模块输出的各种变量名了,同时命令后面也不需要使用大括号了。

exportDefault();

  3、在一个文件或模块中,export、import可以有多个,但 export default只能有一个。

// index_export_default.js
// 在一个文件或模块中,export、import可以有多个,但 export default只能有一个。
export function testFunc() {
    console.log(`export testFunc index_export_default`);
}

var func = function () {
    console.log(`export test_func index_export_default`);
};
export { func as test_func };

export default function () {
    console.log(`index_export_default`);
}
// index_import.js
// import语句中,允许同时输入默认方法和其他接口。
import exportDefault, {testFunc, test_func} from './index_export_default.js';

testFunc();
test_func();
exportDefault();

ES6 - 基础学习(19): 模块化 导出与导入_第1张图片 

  4、export default中的 default是对应的导出接口变量。

// export default命令用于指定模块的默认输出。因此一个模块只能有一个默认输出,从而一个模块 export default命令也只能使用一次;所以,import命令后面才不用跟大括号,因为只可能唯一对应 export default命令。
// 1、本质上,export default就是输出一个名称为 default的变量或方法,所以它后面不能跟变量声明语句。
export default var tempPar = 123;                // Uncaught SyntaxError: Unexpected token 'var'

// 2、同样,因为 export default命令的本质是将 default后面的值,赋给 default变量,所以可以直接将值写在 export default之后。
export default 123;

// 3、最后,export default就是输出一个叫做 default的变量或方法,所以系统允许开发者为 default取任意名字。
function testFunc(x, y) {
    return x * y;
}
export {testFunc as default};        // 等同于 export default testFunc;

你可能感兴趣的:(ES6 - 基础学习(19): 模块化 导出与导入)