export特点
1. export的是接口,而不是值
不能直接通过export输出变量值,而是需要对外提供接口,必须与模块内部的变量建立一一对应的关系
let obj = {}let a = 1function foo() { }export obj // 错误写法export a // 错误写法export foo // 错误写法// 需要修改成对象被括起来或者直接导出的形式。export let a = 1 // 正确写法export function foo() { }export { obj, foo } // 正确写法
2. export值的实时性
export对外输出的接口,在外部模块引用时,是实时获取的,并不是import那个
// 导出文件export.jsconst name = 'kingx'// 一秒后修改变量name的值setTimeout(() => name = 'kingx2', 1000)export { name }// 导入文件import.jsimport { name } from './export.js'console.log(name) // kingxsetTimeout(() => {console.log(name) // 'kingx2'}, 1000)
export用法
1. 使用as关键字设置别名
如果不想对外暴露内部变量的真实名称,可以使用as关键字设置别名,同一个属性可以设置多个别名。
const _name = 'kingx';export {_name as name};export {_name as name2};// 在外部文件进行引入时,通过name和name2两个变量都可以访问到“kingx”值。
2. 相同变量名只能够export一次
const _name = 'kingx'const name = 'kingx'export { _name as name }export { name } // `name` has already been exported. Exported identifiers must be unique
3. 尽量统一export
const name = 'kingx'const age = 12const sayHello = function () {console.log('hello')}export { name, age, sayHello }
import特点
如果想要在HTML页面中使用import命令,需要在script标签上使用代码type="module"。
1. 与export的变量名相同
// export.jsconst _name = 'kingx'export { _name as name }// import.jsimport { _name } from './export.js' // 抛出异常import { name } from './export.js' // 引入正常
3. 相同变量名的值只能import一次
// export1.jsexport const name = 'kingx'// export2.jsexport const name = 'cat'// 同时从两个模块中引入name变量,会抛出异常。import { name } from './export1.js'import { name } from './export2.js' // 抛出异常
4. import命令具有提升的效果
import命令具有提升的效果,会将import的内容提升到文件头部。
// export.jsexport const name = 'kingx'// import.jsconsole.log(name) // kingximport { name } from './export.js'
4. 多次import时,只会一次加载
每个模块只加载一次,每个JS文件只执行一次,如果在同一个文件中多次import相同的模块,则只会执行一次模块文件,后续直接从内存读取。
// export.jsconsole.log('开始执行')export const name = 'kingx'export const age = 12// import.jsimport { name } from './export.js'import { age } from './export.js'
import两次export.js文件,但是最终只输出了一次“开始执行”,可以理解为import导入的模块是个单例模式。
5. import的值本身是只读的,不可修改
使用import命令导入的值,不可以修改基本数据类型的值是,相当于一个const常量;不能修改引用数据类型的地址,可以修改属性值。
// export.jsconst obj = {name: 'kingx5',}const age = 15export { obj, age }// import.jsimport { obj, age } from './export.js'obj.name = 'kingx6' // 修改引用指向的值,正常obj = {} // 抛出异常,不可修改引用指向age = 15 // 抛出异常,不可修改值本身
import用法
1. 设置引入变量的别名
同样可以使用as关键字为变量设置别名,可以用于解决上一部分中相同变量名import一次的问题。
// export1.jsexport const name = 'kingx'// export2.jsexport const name = 'cat'// 使用as关键字设置两个不同的别名,解决了问题import { name as personName } from './export1.js'import { name as animalName } from './export2.js'
2. 模块整体导入
当需要导入整个模块的内容时,可以使用星号*配合as关键字指定一个对象,通过对象去访问各个输出值。
// export.jsconst obj = {name: 'kingx',}export const a = 1export { obj }// import.jsimport * as a from './export.js'import {* as a} from './export.js'; // 错误的写法
export default特点
1. 一个文件只有一个export default语句
let defaultParam = 1export default defaultParamexport default 2 // 抛出异常
2. import不需要使用大括号括起来
// 表示引入export.js中默认输出的值import param from './export.js'// 表示引入export.js文件中输出的变量名为param的值import { param } from './export.js文件中'
Module加载的实质
ESModule的运行机制是这样的:当遇到import命令时,不会立马去执行模块,而是生成一个动态的模块只读引用,等到需要用到时,才去解析引用对应的值。由于ES6的模块获取的是实时值,就不存在变量的缓存。
// export.jsexport let counter = 1export function incCounter() {counter++}// import.jsimport { counter, incCounter } from './export.js'console.log(counter) // 1incCounter()console.log(counter) // 2// 这表明导入的值仍然与原来的模块存在引用关系,并不是完全隔断的。
如果在多个文件中引入相同的模块,则它们获取的是同一个模块的引用。
function Counter() {this.sum = 0this.add = function () {this.sum += 1}this.show = function () {console.log(this.sum)}}export let c = new Counter()// import1.jsimport { c } from './export.js'c.add()// import2.jsimport { c } from './export.js'c.show()// 在一个html文件中同时引入两个import文件import './import1.js';import './import2.js';// 结果输出为“1”,因为在两个import文件中使用的c变量指向的是同一个引用
CommonJS和ESModule区别
- CommonJS在运行时完成模块的加载,而ES6模块是在编译时完成模块的加载,效率要更高。
- CommonJS模块是对象,而ES6模块可以是任何数据类型,通过export命令指定输出的内容,并通过import命令引入即可。
- CommonJS模块会在require加载时完成执行,而ES6的模块是动态引用,只在执行时获取模块中的值。
