本文总结自网易云课堂-前端工程微专业课程
模块组织(js脚本)
在构件具有一定规模的项目时,我们往往采用分割的形式,将一个项目中拆分成一个个的小模块,这样在开发和维护时,会简单许多。而在不同的模块中,我们只需考虑模块输出的接口就可以了。但是在ES5及以下版本的规范中,模块并没有原生支持。那么如何编写我们的代码,来巧妙的弥补这一缺陷呢?
模块职责:
封装实现
暴露接口
声明依赖(主要是模块系统职责)
1.反模块
反模块即不使用任何的模块系统,所编写的函数都散落在全局当中,无封装性,也没有明显的接口暴露,调用函数也没有依赖声明。这样的做法是很危险的,因为你的变量在全局中,大家都可以拿来修改,特别是出现重名变量的时候,就会出现冲突。
2.字面量
var math = {
add: function add(a, b){
return a+b;
},
sub: function sub(a, b){
return a-b;
}
}
//声明一个math对象,有两个方法,我们很明显的可以看出,这个模块输出的方法很直观。
var calculate = {
action: "add",
compute: function compute(a, b){
switch(this.action){
case "add": return math.add(a, b);
case "sub": return math.sub(a, b);//调用的时候也很简单。
}
}
}
由上可以看出,字面量清楚的描述了接口是什么,但是却没有访问限制,并且调用时也没有依赖声明。也就是说,这个模块没有被封装起来,我们可以在全局中任意的修改其中的所有属性和方法。
3.IIFE(自执行函数表达式)
var calculate = (function(){
var action = "add";
return{
compute: function compute(a, b){
switch(action){
case "add": return math.add(a, b);
case "sub": return math.sub(a, b);
}
}
}
} )();
//在js中,函数闭包能保存函数的上一级中变量的状态,所以这段代码在执行之后,action变量无法被改变,但却可以被使用(仅在return出的函数中)。
IIFE的方法实现了私有变量的访问控制,但依然没有对应的依赖声明,由此,变出现了下面的这种形式:
var calculate = (function(m){//这里的m即传入的math
var action = "add";
function compute(a, b){
switch(action){
case "add": return m.add(a, b);
case "sub": return m.sub(a, b);
}
}
return{
compute: compute//返回一个名叫compute的方法,这个方法是compute函数
}
})(math);//把math作为自执行函数的参数传入
这样,使用的也是IIFE方法,并且对于暴漏的接口也更方便去查找和更改。并且有了对依赖的声明,只是我们需要手动去管理依赖,并且对于模块的加载顺序也有要求,被依赖的模块必须首先被加载。并且会污染全局变量(var calculate有重名风险)。
4.命名空间
为了避免IIFE污染全局变量的风险,我们采用一种统一命名的方式,来解决这个问题。
var namespace = (function(){//namespace函数用来缓存所有的模块,并且返回当前组件。
var cache = {};//缓存所有模块
function createModule(name, deps, definition){
console.log(arguments.length);
if(arguments.length == 1) return cache[name];
deps = deps.map(function(depName){
return cache[depName];//将依赖组件从cache中抽出
})
//程序的主体部分在此执行,并且把返回的接口保存在cache中
cache[name] = definition.apply(null, deps)//将依赖组件当成参数传入调用函数中,这样,调用函数中便有了依赖组件。
return cache[name];
}
return createModule;
})()
namespace("math", [], function(){//模块声明,依赖声明,以及模块构成
function add(a, b){return a+b}
function sub(a, b){return a-b}
return{//返回需要暴漏的接口
add: add,
sub: sub
}
});
namespace("calculate", ["math"], function(m){
var action = "add";
function compute(a, b){
return m[action](a, b);
}
return{
compute: compute
}
});
但是我们依然需要手动的去管理依赖,那么,使用什么办法才能够解决手动依赖的弊端呢?这就需要使用民间的模块系统来处理了。