JS模块化专题一
模块化发展历史
IE6之前,没有JS引擎,JS属于渲染引擎。IE6之后,出现JS引擎,JS才真正开始发展。
模块化概念产生
当时方式,直接在script标签内写JS脚本。
index.html
<script type="text/javascript">
console.log('JavaScript');
</script>
随着发展,代码量增多,开始使用外部JS,并进行区分。这是最初模块化的概念。
index1.html
<script type="text/javascript" src="js/index1.js"></script>
index2.html
<script type="text/javascript" src="js/index2.js"></script>
外部文件引入
问题:处理处理通用的JS脚本
解决方案:抽离common.js文件,编写公用脚本,以供页面使用。
index1.html
<script type="text/javascript" src="js/common.js"></script>
<script type="text/javascript" src="js/index1.js"></script>
index2.html
<script type="text/javascript" src="js/common.js"></script>
<script type="text/javascript" src="js/index2.js"></script>
公用脚本处理
问题:通过引入common.js,加载某些不必要的JS脚本。
解决方案:不能只以页面为基础,来区分JS文件,需要以JS脚本功能作为区分。
module_a.js
var a = [1, 2, 3, 4, 5].reverse();
module_b.js
var b = a.concat([6, 7, 8, 9, 10]);
module_c.js
var c = b.join('-');
index.js
console.log(a);
console.log(b);
console.log(c);
index.html
<script type="text/javascript" src="js/module_a.js"></script>
<script type="text/javascript" src="js/module_c.js"></script>
<script type="text/javascript" src="js/module_c.js"></script>
<script type="text/javascript" src="js/index.js"></script>
存在问题
- JS加载顺序必须以模块之间逻辑相关;
- 引入模块文件使用一个JS作用域(全局作用域);
- 变量重名导致变量覆盖问题,会污染全局;
模块化解决的本质问题
- 加载顺序
- 污染全局
关于污染全局
- 污染全局并不是只要在全局声明变量就是污染全局;
- 所有可控的声明在全局中不叫污染全局;
- 在全局声明数据类型的变量才是真正的污染全局;
var a = 123;
var b = [];
var c = {};
模块独立、相互依赖
问题:全局污染
解决方案:使用立即执行函数,创建模块的独立作用域。
立即执行函数
立即执行函数也叫做自启动函数和自执行函数。
函数不是表达式,无法立即执行,通过()的形式让函数声明成为表达式。
;(function () {
// 独立作用域和执行期上下文
// 模块的独立作用域
})();
使用立即执行函数
解决污染全局问题和模块依赖问题,实现按需调用,互不干扰。
但是并没有解决加载顺序问题。
module_a.js
var moduleA = (function () {
var a = [1, 2, 3, 4, 5].reverse();
return {
a: a
}
})();
module_b.js
var moduleB = (function (moduleA) {
var b = moduleA.concat([6, 7, 8, 9, 10]);
return {
b: b
}
})(moduleA);
module_c.js
var moduleC = (function (moduleB) {
var c = moduleB.join('-');
return {
c: c
}
})(moduleB);
index.js
var moduleC = (function (moduleB) {
var c = moduleB.join('-');
return {
c: c
}
})(moduleB);
index.html
<script type="text/javascript" src="js/module_a.js"></script>
<script type="text/javascript" src="js/module_c.js"></script>
<script type="text/javascript" src="js/module_c.js"></script>
<script type="text/javascript" src="js/index.js"></script>
插件化思想
插件化思想,开发者通过一遍又一遍的试验来定义的。
案例:计算器功能插件
index.js
;(function (doc) {
var Calculator = function () {
this.oCalculator = doc.getElementsByClassName('J_calculator')[0];
this.oFirstInput = this.oCalculator.getElementsByTagName('input')[0];
this.oSecondInput = this.oCalculator.getElementsByTagName('input')[1];
this.oResult = this.oCalculator.getElementsByClassName('result')[0];
this.init();
}
Calculator.prototype.init = function () {
this.bindEvent();
}
Calculator.prototype.bindEvent = function () {
this.oCalculator.addEventListener('click', this.onBtnClick.bind(this), false);
}
Calculator.prototype.onBtnClick = function (ev) {
var e = ev || window.event,
tar = e.target || e.srcElement,
tagName = tar.tagName.toLowerCase();
if (tagName === 'button') {
var field = tar.getAttribute('data-field'),
val1 = Number(this.oFirstInput.value) || 0,
val2 = Number(this.oSecondInput.value) || 0;
this.oResult.innerHTML = this.calculate(field, val1, val2);
}
}
Calculator.prototype.calculate = function (field, val1, val2) {
switch (field) {
case 'plus':
return val1 + val2;
case 'minus':
return val1 - val2;
case 'mul':
return val1 * val2;
case 'div':
return val1 / val2;
}
}
window.Calculator = Calculator;
})(document);
index.html
<div class="J_calculator">
<p>
<input type="text" placeholder="第一个数字" />
<input type="text" placeholder="第二个数字" />
</p>
<p>
计算结果:<span class="result">0</span>
</p>
<p>
<button data-field="plus">+</button>
<button data-field="minus">-</button>
<button data-field="mul">*</button>
<button data-field="div">/</button>
</p>
</div>
<script type="text/javascript" src="./js/index.js"></script>
<script type="text/javascript">
new Calculator();
</script>