1. 模块化
模块化的理解
什么是模块化
- 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件),并进行组合在一起
- 块的内部数据与实现(函数)是私有的,只是向外暴露一些接口(方法)与外部其他模块通信
模块化的好处
- 避免命名冲突
- 更好的分离,按需加载
- 更高的复用性
- 高可维护性
模块化规范
CommonJS
NodeJs
应用由模块组成,采用CommonJS
。每个文件就是一个模块,有自己的作用域。在一个文件里定义的变量 函数、类都是私有的,对其他文件不可见。
在服务器端,模块的加载时运行时同步加载;在浏览器端,模块需要提前编译打包处理。
特点
- 所有代码运行在模块作用域,不污染全局作用域
- 模块可以多次加载,但只会在第一次加载时运行一次,并缓存起来。再加载,就直接读取缓存结果。要想让模块再次运行,必须清楚缓存。
- 模块加载的顺序,按照其在代码中出现的顺序。
基本语法
- 暴露模块:
module.exports = value
或exports.xxx = value
- 引入模块:require(name/path)
- name: 第三方模块名
- path:自定义模块文件路径
CommonJS规范规定,每个模块内部,module变量(是个对象)代表当前模块。module的exports属性(module.exports)是对外的接口
加载某个模块其实是加载某个模块的module.exports
属性
js
var x = 5
var addR = function(value) {
return value + x
}
module.exports.x = x
module.exports.addX = addX;
上面代码通过module.exports输出变量x和函数addX
js
var example = require('./example.js')
console.log(example.x)
console.log(example.addX())
CommonJS中一旦输出一个值,模块内部的变化就影响不到这个值
js
// lib.js
var counter = 3
function incCounter() {
counter += 1
}
module.exports = {
counter,
incCounter
}
js
// main.js
var counter = require('./lib').counter
var incCounter = require('./lib').incCounter
console.log(counter) // 3
incCounter()
console.log(counter) // 3
// counter是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。
AMD
CommonJS加载模块是同步的(只有加载完成才能完成后面的操作)。AMD是非同步加载,可以指定回调函数。
NodeJS主要用于服务端,模块文件一般存在本地,加载较快。如果是浏览器环境,要从服务器加载模块,就必须采用非同步模式,所以浏览器端一般采用AMD模式
基本语法
定义暴露模块:
js
// 定义没有依赖的模块
define(function(){
return 模块
})
// 定义有依赖的模块
define(['module1', 'module2'], function(m1, m2) {
return 模块
})
引入使用模块:
js
define(['module1', 'module2'], function(m1, m2) {
// 使用m1 m2
})
CMD
CMD规范专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行。CMD规范整合了CommonJS和AMD规范的特点。在 Sea.js 中,所有 JavaScript 模块都遵循 CMD模块定义规范。
基本语法
定义暴露模块:
js
// 定义没有依赖的模块
define(function(require, exports, module) {
export.xxx = value
module.exports = value
})
// 定义有依赖的模块
define(function(require, exports, module) {
//引入依赖模块(同步)
var module2 = require('./module2')
//引入依赖模块(异步)
require.async('./module3', function (m3) {
})
//暴露模块
exports.xxx = value
})
引入使用模块:
js
define(function (require) {
var m1 = require('./module1')
var m4 = require('./module4')
m1.show()
m4.show()
})
ES6模块化
ES6模块化语法
export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能
js
// 定义模块 math.js
var basicNum = 0
var add = function(a, b) {
return a + b
}
export {basicNum, add}
js
// 引用模块
import {basicNum, add} from './math'
function test(ele) {
ele.textContent = add(99 + basicNum)
}
如上例所示,使用import
命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default
命令,为模块指定默认输出。
js
// exportDefault.js
export default function() {
console.log('foo')
}
js
// importDefault.js
import test from './exportDefault'
test() // foo
ES6模块化和CommonJS模块化的差别
- CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用
- CommonJS模块是运行时加载;ES6模块是编译时输出接口:因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。