导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

冷门技能

为什么用到 IIFE

立即执行函数执行完后作用域销毁,自身的 AO,GO 都销毁,只剩下 return 返回值和返回值持有的函数中的变量的引用(e.g. 闭包) 做到了变量的私有化

var module = (function(moduleA) { // 把参数缓存为私有变量
	const obj = {
  	m1,
    m2
  }
  
  return {
    obj
})(window.moduleA || {}); // 👈 实现模块注入,模块参数一目了然

缺点

Untitled

模块化

模块化发展

无模块化 → CommonJS规范 → AMD规范 → CMD规范 → ES6模块化

无模块劣势

<script src="jquery.js"></script>
<script src="jquery_scroller.js"></script>
<script src="main.js"></script>
...

缺点:
被依赖的放在前面,否则使用就会报错
污染全局作用域
维护成本高
依赖关系不明显

CommonJS 规范(NodeJS)

// 定义模块math.js
var basicNum = 0;
function add(a, b) {
  return a + b;
}
module.exports = { //在这里写上需要向外暴露的函数、变量
  add: add,
  basicNum: basicNum
}

// 引用自定义的模块时,参数包含路径,可省略.js
var math = require('./math');
math.add(2, 5);

// 引用核心模块时,不需要带路径
var http = require('http');
http.createService(...).listen(3000);

exports 是对 module.exports 的引用。比如我们可以认为在一个模块的顶部有这句代码: exports = module.exports 所以,我们不能直接给 exports 赋值:

✅ exports.foo = 'bar'
❌ exports = {foo: 'bar'} //error 这种方式是错误的,相当于重新定义了exports

优点

解决了依赖、全局变量污染的问题

缺点

CommonJS 用同步的方式加载模块,这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。所以不适合浏览器端模块加载,更合理的方案是使用异步加载。

AMD规范(RequireJS)

AMD规范采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

/** 网页中引入require.js及main.js **/
<script src="js/require.js" data-main="js/main"></script>

/** main.js 入口文件/主模块 **/
// 首先用config()指定各模块路径和引用名
require.config({
  baseUrl: "js/lib",
  paths: {
    "jquery": "jquery.min",  //实际路径为js/lib/jquery.min.js
    "underscore": "underscore.min",
  }
});
// 执行基本操作
require(["jquery","underscore"],function($,_){
  // some code here
});

优点

适合在浏览器环境中异步加载模块、并行加载多个模块

缺点

必须要提前加载所有的依赖,然后才可以使用,而不是需要使用时再加载。(不能按需加载

CMD(SeaJS)

与AMD类似。

不同点在于:

CMD 与 AMD 区别

/** AMD写法 **/
define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { 
     // 等于在最前面声明并初始化了要用到的所有模块
    a.doSomething();
    if (false) {
        // 即便没用到某个模块 b,但 b 还是提前执行了
        b.doSomething()
    } 
});

/** CMD写法 **/
define(function(require, exports, module) {
    var a = require('./a'); //在需要时声明
    a.doSomething();
    if (false) {
        var b = require('./b');
        b.doSomething();
    }
});

ES6 模块化

ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,旨在成为浏览器和服务器通用的模块解决方案。其模块功能主要由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

/** 定义模块 math.js **/
var basicNum = 0;
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };

/** 引用模块 **/
import { basicNum, add } from './math';
function test(ele) {
    ele.textContent = add(99 + basicNum);
}

es6 在导出的时候有一个默认导出 export default ,使用它导出后,在 import 的时候,不需要加上 {} ,模块名字可以随意起。该名字实际上就是个对象,包含导出模块里面的函数或者变量。

/** export default **/
//定义输出
export default { basicNum, add };
//引入
import math from './math';
function test(ele) {
    ele.textContent = math.add(99 + math.basicNum);
}

模块化开发怎么做?

立即执行函数,不暴露私有成员

var module1 = (function(){
  var _count = 0;
  var m1 = function(){
    //...
  };
  var m2 = function(){
    //...
  };
  return {
    m1 : m1,
    m2 : m2
  };
})();

参考