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