ES Module规范
认识ES Module
ES Module和CommonJS的模块化有一些不同之处:
- 一方面它使用了import和export关键字;
- 另一方面它采用编译期的静态分析,并且也加入了动态引用的方式;
ES Module模块采用export和import关键字来实现模块化:
- export负责将模块内的内容导出;
- import负责从其他模块导入内容;
采用ES Module将自动采用严格模式:use strict
- 注意事项一:在浏览器中使用ES Module时,必须在文件后加上后缀名 .js
const name='abc'
const age=12
function sayHello(){
console.log('hello')
}
//导出
exports {
name,age,sayHello
}
引入时的路径必须包含后缀名!
import {name,age,sayHello} from './index.js'
- 注意事项二:在打开对应的html文件,如果html中有使用模块化的代码,则必须开启一个服务来打开
如果直接在浏览器中运行代码,会报如下错误:
exports关键字
export关键字将一个模块中的变量、函数、类等导出
导出的方式:
- 在语句声明的前面直接加上export关键字
export const name='孙悟空'
export age=18
export function sayHello(){
console.log('hello')
}
- 将所有需要导出的标识符,放到export后面的{}中
export const name='孙悟空'
export age=18
export {
name,age
}
注意:这里的**{}里面不是ES6的对象字面量的增强写法**,{}也不是表示一个对象的;
- 导出时给标识符起一个别名——通过as关键字起别名
export const name='孙悟空'
export const age=18
export {
name as fname,
age as fage
}
导入时应导入别名:
import {fname,fage} from './index.js'
import关键字
import关键字负责从另外一个模块中导入内容
导入方式:
- import {标识符列表} from ‘模块’;
import {name,age} from './index.js'
这里的{}也不是一个对象,里面只是存放导入的标识符列表内容;
- 导入时给标识符起别名——通过as关键字起别名
import {name as fname,age} from './index.js'
console.log(fname)
- 通过*将模块功能放到一个模块功能对象(a module object)上,将整个模块导入进来
const name='孙悟空'
const age=18
export {
name,age
}
导入时:
import * as index from './index.js'
console.log(index.name,index.age)
export和import结合使用
//从index.js文件(模块)中引入name、age并导出
export {name,age} from './index.js'
//从index.js文件(模块)中引入所有并导出
export * from './index.js'
default用法
前面我们学习的导出功能都是有名字的导出(named exports):
- 在导出export时指定了名字;
- 在导入import时需要知道具体的名字;
还有一种导出叫做默认导出(default export)
- 默认导出export时可以不需要指定名字;
- 在导入时不需要使用{},并且可以自己来指定名字;
- 它也方便我们和现有的CommonJS等规范相互操作;
注意:在一个模块中,只能有一个默认导出(default export);
function parseLyric(){
return '歌词'
}
//默认导出
export default parseLyric
也可以直接定义标识符直接作为默认导出:
export default function(){
return '歌词'
}
如果导出方式是默认导出,则导入时不需要使用大括号,并且可以随便起一个变量名接收
import aaa from './parse.js'
import函数
通过import加载一个模块(import声明语法),是不可以在其放到逻辑代码中的,比如:
let flag=true
if(flag){
//不允许在逻辑代码中编写import导入声明语法,只能写到js代码顶层
import {name,age} from './index.js'//会报错!
}
原因:
- 这是因为ES Module在被JS引擎解析时,就必须知道它的依赖关系;
- 由于这个时候js代码没有任何的运行,所以无法在进行类似于if判断中根据代码的执行情况;
但是某些情况下,我们确确实实希望动态的来加载某一个模块,比如:根据不同的条件,动态来选择加载模块的路径;
这个时候我们需要使用import()函数来动态加载:
let flag=true
if(flag){
import('./index.js').then(res=>{
console.log(res.name,res.age)
})
}else{
import('./bbb.js').then(res=>{
res.sayHelo()
})
}
import meta
import.meta是一个给JavaScript模块暴露特定上下文的元数据属性的对象:
- 它包含了这个模块的信息,比如说这个模块的URL;
- 在ES11(ES2020)中新增的特性;
ES Module的解析过程
ESModule的解析过程可以划分为三个阶段:
- 构建(Construction),根据地址查找js文件,并且下载,将其解析成模块记录(ModuleRecord);
- 实例化(Instantiation),对模块记录进行实例化,生成模块环境记录,并且分配内存空间,解析模块的导入和导出语句,把模块指向对应的内存地址。
- 运行(Evaluation),运行代码,计算值,并且将值填充到内存地址中;