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>

存在问题

  1. JS加载顺序必须以模块之间逻辑相关;
  2. 引入模块文件使用一个JS作用域(全局作用域);
  3. 变量重名导致变量覆盖问题,会污染全局;

模块化解决的本质问题

  1. 加载顺序
  2. 污染全局

关于污染全局

  1. 污染全局并不是只要在全局声明变量就是污染全局;
  2. 所有可控的声明在全局中不叫污染全局;
  3. 在全局声明数据类型的变量才是真正的污染全局;
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>