前言
对于流行的语言,类似于python、java、c等等,在初期学习,我们一般就能接触到他们的模块管理模式,大概了解其运行的机制。而JavaScript,由于天生的缺陷,随着学习的深入,许久后才能了解到一些列诸如UMD、CommonJS和ES6 Module等看起来相似却又迥异的多种模块管理模式。
模块管理
JavaScript在现如今前端开发越来越复杂的,原先的设计模式中,模块管理的缺乏,带来了各种不方便。也导致了诸多的模块管理工具的产生。
模块化的主要特征是:
- 模块化,可重用
- 封装了变量和function,和全局的namaspace不接触,松耦合
- 只暴露可用public的方法,其它私有方法全部隐藏
实现方式
函数包裹
平时写代码时,写的一个个函数,就相当于一个小的模块,从这个出发,来思考模块的设计模式。
逻辑放在一个个函数里,封装在一个文件中,需要时直接引入。
1 | function f1() {} |
优点:对代码进行了简单封装复用,挂载全局变量,可直接调用。
缺点: 污染了全局变量,模块成员之间看不出联系。使用一个函数把其他没使用到的函数也引入了。
对象封装
为了解决对全局变量的污染,可以把他们写到一个对象里面,函数变为这个对象的一个个方法。
1 | var module1 = { |
优点:包裹了函数,对于全局变量的污染没有那么大。
缺点:所有成员直接暴露给外部,内部的状态可以被外部改写。
闭包
使用JavaScript里面特有的闭包属性,在函数的运行周期里,闭包保证了内部代码始终处于私有状态下。
1 | function module() { |
优点:比较好的保护了私有变量
缺点:动态添加方法比较麻烦,无法修改内部私有变量。
模块依赖方法对比
CommonJs
Node.js是commonJS规范的主要实践者,它有四个重要的环境变量为模块化的实现提供支持:module
、exports
、require
、global
。实际使用时,用module.exports
定义当前模块对外输出的接口(不推荐直接用exports
),用require
加载模块。
1 | // module.js |
ES6 Module
ES6 在语言标准的层面上,终于实现了模块功能。其模块功能主要由两个命令构成:export
和import
。export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。
1 | // 定义模块 module.js |
ES6的模块不是对象,import
命令会被 JavaScript 引擎静态分析,在编译时就引入模块代码,而不是在代码运行时加载,虽然无法实现条件加载,但让静态分析成为可能。
ES6 模块与 CommonJS 模块的差异
在讨论差异前,必须了解ES6 模块与 CommonJS 模块完全不同。
它们有两个重大差异。
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
第一个是差异ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
第二个差异是因为 CommonJS 加载的是一个对象(即module.exports
属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
后记
这个系列主要是为了帮助自己梳理前端的知识,虽然有加上自己的理解写了一些,但自己的思考还是少了点,更多的是从文档、别人的文章收集整理的=-=有点不太好意思,但还是一个学习的过程吧。以后还会不断补充,有了新的想法后会记上。
坚持学习,加油。
Credits
阮一峰ECMAScript 6 入门:Module 的加载实现