无模块化
- 原始写法-简单罗列
function m1(){
//...
}
function m2(){
//...
}
缺点: 污染了全局变量,无法保证不与其它模块之前发生变量名冲突,而且模块之前看不出直接关系
- 对象写法-类似命名空间
把模块写成一个对象,所有模块成员放在这个对象里,调用的时候通过module1.m1的方式调用
var module1 = new Object({
_count : 0,
m1 : function (){
//...
},
m2 : function (){
//...
}
});
优点: 一定程度上解决了变量名的冲突问题 缺点: 会暴露素有的模块成员,内部状态可以被外部改写,比如外部代码可以直接改变内部计数器的值。module1._count=5
- 立即执行函数写法(IIFE)
var module1 = (function(){
var _count = 0;
var m1 = function(){
//...
};
var m2 = function(){
//...
};
return {
m1 : m1,
m2 : m2
};
})();
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
<script src="4.js"></script>
<script src="5.js"></script>
<script src="6.js"></script>
优点: 使用这种方法,可以达到不暴露私有成员的目的 缺点: 多个一起使用时,需要保证调用顺序
- 增强版立即执行函数
(function(window,$){
let msg = 'modules3',
function foo(){
console.log('foo()')
}
window.module4=foo
$(body).css(background,'#f00')
})(window,jQuery);
优点: 当在函数内部需要使用全局变量的时候,显式的将变量输入了模块,保证了模块的独立性,使得模块间的依赖关系变的明显 缺点: 同上,当然这个式所有无模块阶段共同的问题
为什么出现模块化
- 代码复用
- 避免全局作用域污染
- 可维护性:当依赖关系很复杂的时候,以上代码的编写和维护都会变得困难
- 更好的分离,按需加载
commonJS规范
09年出现commonJs,每个文件都可以看作是一个模块,内部定义的变量就属于这个模块,只有暴露出的变量外部引入后才能访问
使用方法
- 通过require引入模块
- 通过module.exports或者exports导出模块
//a.js
var num = 100;
var add = function(val){
return val + num
}
module.exports={
num,
add
}
// exports.num2 = 200
//b.js
var moduleA = require('./a.js')
var fn = moduleA.add;
优点: 解决了依赖、全局变量污染的问题 缺点:同步加载模块。在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,CommonJS不适合浏览器端模块加载,更合理的方案是使用异步加载
深入了解CommonJS
在任意一个js中打印以下代码
console.log(this)
console.log(arguments)
console.log(module.exports,exports,require, module, __filename,__dirname)
console.log(module.exports=== exports)
// true
module.exports和exports的区别
- 未赋值前,module.exports和exports指向同一个引用对象{},他们是全等的
- module.exports赋值后,指向了新的对象,他们俩之后就不相等了,建议,同一个文件中,只使用一种,避免造成混乱
commonJs在浏览器端是怎么使用的
- 上边我们说过commonJs不适用于浏览器,但我们在写代码的时候不少见到这种写法的使用,其实是因为webpack等构建工具,帮我们进行打包转化之后以script标签的形式引在了html页面中
AMD规范
承接上文,AMD规范支持异步加载模块,允许指定回调函数,AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。 AMD标准中,定义了下面三个API:
- require([module], callback)
- define(id, [depends], callback)
- require.config()
即通过define来定义一个模块,然后使用require来加载一个模块, 使用require.config()指定引用路径。 先到require.js官网下载最新版本,然后引入到页面,如下:
// html页面入口,requirejs加载之后开始执行main.js
<script src="js/require.js" data-main="js/main"></script>
// main.js头部配置
(function(){
requirejs.config({
baseUrl: "js/lib",
paths: {
"jquery": "jquery.min",
"dataService": "./modules/dataService",
"alerter": "./modules/alerter",
}
})
requirejs(['alerter'],function(alerter){
alerter.showMsg()
})
})()
// 定义没有依赖的模块
//dataService.js
define(function(){
let name = 'dataService.js'
function getName(){
return name
}
return {getName}
})
// alerter.js
// 定义有依赖的模块
define(['dataService'],function(dataService){
let msg = 'alerter.js'
function showMsg(){
console.log(msg,dataService,getName)
}
return {showMsg}
})
引用第三方库的时候
- 如果库本身支持AMD,比如jquery
- 库本身不支持,需要用shim暴露一个变量出来 优点:适合在浏览器环境中异步加载模块、并行加载多个模块
缺点:不能按需加载、开发成本大
CMD规范
支持按需加载,是 SeaJS 在推广过程中对模块定义的规范化
使用方法
1. 引入sea.js库
2. 如何定义导出模块
define()
exports
module.exports
3. 如何定义依赖模块
require()
4. 如何使用模块
seajs.use('./js.moduels/main.js')
// html页面引用
<script type='text/javascript' src='js/libs/sea.js'></script>
<script type='text/javascript'>
seajs.use('./js.moduels/main.js')
</script>
ES6规范
- es6采用了静态话的编译思想,使得在编译时就能确定模块的依赖关系,以及输入输出的变量。
- es6的模块自动采用严格模式,不管有没有在模块头部加上‘use strict’
基本使用方法
- 导出
- 导出单个变量
export const a
export function b(){}
- 导出整个模块
export {
a,
b
}
- 导出模块重命名
export {a,b as func}
- 引入
- 引入几个变量
import {a,b} from './a.js'
- 引入模块别名方式
import * as obj from './a.js'
- 其中某个变量重命名
import {a,b as func} from './a.js'
- default语法
- 导出匿名函数
export default function(){}
// 导入可以给其它字段赋值
import test from './a.js'
- 导出具名函数
function c(){}
export default c
// 导出函数重命名
export {c as default}
动态加载方式
- import()返回一个Promise对象
import('./module/a.js')
.then(module=>{
……
})
.catch(err=>{
console.log(err.message)
})
- 同时想加载多个模块,可以用以下写法
Promise.all([
import('./module1.js')
import('./module2.js')
import('./module3.js')
])
.then(([module1,module2,module3])=>{
……
})
- import()也可以用在async函数中
async function main(){
const myModule = await import('./module1.js')
}
main()
CommonJs和ES6区别
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。