
javascript用共享一切的方法加载代码,这是这个语言中最容易出错且容易令人感到困惑的地方。在ES6之前,应用程序的每一个Javascript中定义的一切都共享一个全局的作用域,容易引发命名冲突和安全问题。
为了解决作用域问题,使JS应用程序变得有序,便引进了模块。
在ES6模块规范出现前,浏览器原生并不支持模块的行为,ECMAScript同样不支持模块,因此希望使用模块模式的库或代码库必须基于JavaScript的语法和词法特性’伪造’出类似模块的行为
模块思想
把逻辑分块,各自封装,相互独立,每个块自行决定对外暴露的内容,同时自行决定引入执行哪些外部代码
模块标识符
模块系统本质上是键值实体,其中每个模块都有个可用于引用它的标识符。
原生浏览器模块标识符必须提供实际JS文件的路径。除了文件路径,Node.js还会搜索node_modules目录,用标识符去匹配包含index.js的目录
模块依赖
模块系统的核心是管理依赖。指定依赖的模块与周围的环境会达成一种契约。本地模块向模块系统声明一组外部模块,这些外部模块对于当前模块正常运行是必需的。模块系统检视这些依赖,确保这些外部模块能够被加载并在本地模块运行时初始化所有依赖。
模块加载
加载模块的概念派生自依赖契约,当一个外部模块被指定为依赖时,本地模块期望在执行它时,依赖已准备好并已初始化。
浏览器会递归地评估并加载所有依赖,知道所有依赖模块都加载完成。
只有整个依赖图都加载完成,才可以执行入口文件
模块加载是阻塞的,意味着前置操作必须完成才能执行后续操作。每个模块在自己的代码到达浏览器之后完成加载,此时其依赖已经加载并初始化。
动态依赖
有的模块系统要求开发者在模块开始列出所有依赖,有些允许开发者在程序结构中动态添加依赖。
动态添加的依赖必须在模块执行前加载完毕。虽然支持更复杂的依赖关系,但增加了对模块进行静态分析的难度
静态分析
模块中包含的要发送到浏览器的JavaScript代码经常会被静态分析,分析工具会检查代码结构并在不实际执行代码 的情况下推断其行为。
循环依赖
所有的模块系统都支持循环依赖,在包含循环依赖的程序中,模块的加载顺序可能会出人意料,都是执行深度优先的依赖加载
ES6模块
ES6最大的一个改进是引入了模块规范
模块是自动运行在严格模式下并且没有办法退出运行的JS代码。
在模块顶部创建的变量不会自动添加到全局共享作用域,仅在该模块的顶级作用域中存在。
在模块顶部this值为undefined
模块不支持HTML风格的代码注释
ES6模块是异步加载执行的
脚本,也就是任何不是模块的JS代码,缺少这些特性
导出和导入
使用export关键字暴露相关代码给其他模块
export let name='yqx'
function sum(num1,num2){
return num1 + num2
}
export { sum as add } // 可重命名
export default sum; // 可导出默认值
// 导入
import sum,{ name , add } from "导出的文件相对路径" // 默认值必须排在非默认值前面
可以为每个模块设置一个默认的导出值
import语句为变量、函数和类创建的是只读绑定,因此不能给导入的绑定重新赋值
export和import必须在其他语句和函数之外使用,因此不能动态的导入导出
导入导出都可重命名
export {sum} from '相对路径' // 重新导出一个绑定
模块可以不导出任何东西,通过无绑定导入执行模块中的代码。一般用于创建Polyfill和Shim
加载模块
ES6只是定义了模块的语法,但它并没有定义如何加载模块,这正是规范复杂性的一个体现,应由不同的实现环境来决定
在Web浏览器中使用模块
Web浏览器有多种方式将JS包含在Web应用程序中
- 在script元素中通过src属性指定加载代码的地址
- 将JS代码内嵌到没有src属性的srcript元素中
- 通过Web Worker 或Service Worker的方法加载并执行JS文件
在script元素中使用模块
当type属性的值为module时支持加载模块,当无法识别type的值时,浏览器会忽略script元素
模块加载顺序
当type=”module”时自动应用defer属性。一旦HTML解析器遇到模块类型便开始下载模块文件,知道文档被完全解析模块才会执行。并且模块按照他们在文档中出现的顺序执行
async属性也可以被应用在模块上,需要确保当模块执行前,其所需的所有资源都下载完成,但不能保证的时模块的执行时机。
将模块作为worker加载
通过使用Web Workers,Web应用程序可以在独立于主线程的后台线程中,运行一个脚本操作。这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是UI线程)不会因此被阻塞/放慢。
let worker1=new Worker("script.js")
let worker2=new Worker("module.js",{type:"module"})
与CommonJS的区别
| CommonJS | es模块 | |
|---|---|---|
| 指定依赖 | require | import |
| 导出 | module.exports | export |
| 动态加载 | 可以 | 不可以 |