媒介
愿望编写程序能像玩积木一样,起首计划要产出如何的作品,然后在积木堆中遴选适宜的积木块,末了一组合就落成了。
因而JavaScript须要相似如许模块化,每一个模块都隐蔽内部细节而且对外暴露接口,再处置惩罚好模块之间的依靠关联,就可以到达玩积木的结果了。
虽然现有许多模块化框架/东西,但关于新手来讲,为何不本身撸一个简朴的模块化东西呢?
愿望经由过程这个东西把本身以为好用的代码以模块的体式格局构造起来,逐渐构成本身的JS库,以后可以勇敢地和HR说,本身的小项目用的是本身小JS库,^_^。我以为,在这个封装的过程当中,新手能进修到许多东西。
新手嘛,多造轮子总是有优点的,=_=。
从闭包到模块
以下是《你所不知道的JavaScript(上卷)》中关于闭包的申明。
当函数可以记着并接见地点的词法作用域,纵然函数是在当前词法作用域以外实行,这时刻就产生了闭包。
实在,不管如何,闭包正如其字面意义一样,既能供应一个相对关闭的空间,也能向外界暴露必要的接口。这不就正相符我们模块化的需求吗?
在此,发起参考这篇文章,以增强您对闭包的明白:《假如手艺HR问您JavaScript的“闭包”,嘿嘿嘿,举这个例子就够了》。
最简朴的模块
var test = (function test(){
function run(){
console.log("run test");
}
return {
run: run
};
})();
test.run();
上面的代码有个叫test的函数作为模块建立器,每次挪用它都可以建立一个新的模块。这里运用马上实行函数,马上建立了一个test模块。参考闭包的观点,外部可以挪用test模块中的run函数,同时test模块又有本身自力的作用域。能到达一个积木块(模块)的请求。
简朴的模块如许写没有题目,然则模块间的依靠题目没有解决。
最简朴的模块治理东西
模块之间必然会存在依靠关联,而模块治理东西须要可以很好地治理模块间的依靠。下面我们模拟完成了AMD范例的东西requirejs,主假如模拟其define,get的API作风,本身写一个简朴的版本。
//模块治理东西,MyModules
var MyModules = (function Manager() {
var modules = {};
function define(name, deps, impl) {
for (var i=0; i<deps.length; i++){
//将依靠的名字替换成已注册了的模块
deps[i] = modules[deps[i]];
}
//将依靠数组展开成参数传入模块的构建函数,天生新模块
modules[name] = impl.apply(impl, deps);
}
function get(name){
return modules[name];
}
return {
define: define,
get: get
}
})();
//定义一个模块,data
MyModules.define("data",[],function(){
var name = "miku";
function getName(){
return name;
}
return {
getName:getName
}
});
//定义一个模块,app
//该模块依靠data模块
MyModules.define("app", ["data"], function(data){
function run(){
console.log(data.getName());
}
return {
run:run
}
});
//掏出app模块
var app = MyModules.get("app");
//挪用app模块的run要领
app.run();
可以看出,应用MyModules可以很轻易地定义运用模块,治理模块依靠。然则还存在一个题目,MyModules关于模块定义的递次有请求。以上面的例子来讲,就是app模块依靠data模块,那data模块必须在app模块之前被定义。这个限定让我们实际运用中不是很轻易。接下来我们将革新它。
革新模块治理东西
我们须要让模块治理东西不须要限定模块的定义递次,这里我的做法是,运用一个rebuilds数组来保留未胜利构建的模块。每次有新模块构建胜利的时刻就会重新尝试去构建全部rebuilds数组中的模块。详细看下面的代码。
window.mm_modules = (function Manager() {
var debug = false;
var modules = {};
var rebuilds = [];
function copyArray (array){
var tempArray = [];
for(var i=0; i<array.length; i++){
tempArray.push(array[i]);
}
return tempArray;
}
function define(name, deps, impl) {
//推断依靠构建是不是胜利
var depsDone = true;
//拷贝一份当前想要构建的模块,当构建失利时运用
var rebuildCopy = {
name : name,
deps : copyArray(deps),
impl : impl
};
//轮回依靠数组,构建依靠
for (var i=0; i<deps.length; i++){
//将依靠的名字替换成已注册了的模块
deps[i] = modules[deps[i]];
//有依靠的模块未定义,所以这个模块构建失利
if(!deps[i]){
depsDone = false;
break;
}
}
//假如依靠构建胜利,即模块构建胜利
if(depsDone){
//将依靠数组展开成参数传入模块的构建函数,天生新模块
modules[name] = impl.apply(impl, deps);
//从rebuilds数组中移除
if(rebuilds[name]){
delete rebuilds[name];
}
//轮回rebuilds数组,尝试重新构建之前构建失利的模块
for (key in rebuilds){
var rebuild = rebuilds[key];
if(rebuild){
//递归挪用
define(rebuild.name, rebuild.deps, rebuild.impl);
}
}
}
//模块构建失利,存入rebuilds数组中,守候下一次重新构建
else{
rebuilds[name] = rebuildCopy;
}
if(debug){
console.log("[mm_modules debug]need rebuild modules:", rebuilds);
}
}
function get(name){
return modules[name];
}
return {
define: define,
get: get
}
})();
革新后的模块治理东西,可以自动地处置惩罚模块依靠,而不须要限定定义递次了。
那,能不能更进一步呢?试着想一下,我们日常会怎样运用?单文件单模块,然后把这些文件放在差别文件夹里构造好。因而,我就想到运用gulp如许的东西辅佐我们。
gulp辅佐
请参考下面的目次构造。
├── dist
│ ├── index.html
│ └── js
│ └── mm-modules-build.js
├── gulpfile.js
├── mm-modules
│ ├── queryObject.js
│ ├── request.js
│ ├── template.js
│ ├── test.js
│ └── util.js
├── mm-modules.js
可以在mm-modules下随便地定义模块,如util模块内有种种东西函数,template模块则包含了artTemplate模版引擎。以后应用gulp将mm-modules.js(模块治理东西)与mm-modules下一切的模块文件打包成mm-modules-build.js。项目中只需引入mm-modules-build.js即可。
末端
到此,我们本身构建了一个很有用的JavaScript模块化东西,项目的源码在这里:https://github.com/MIKUScallion/mm-modules,喜好的话,给个✨。
再回忆一下媒介的话。
愿望经由过程这个东西把本身以为好用的代码以模块的体式格局构造起来,逐渐构成本身的JS库,以后可以勇敢地和HR说,本身的小项目用的是本身小JS库,^_^。我以为,在这个封装的过程当中,新手能进修到许多东西。
新手嘛,多造轮子总是有优点的,=_=。
参考
《你所不知道的JavaScript(上卷)》