本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-12-15
模块化:将复杂的系统分解为高内聚、低耦合的模块,使系统开发变的可控、可维护、可拓展,提高模块的复用率。
同步模块模式(SMD: Synchronous Module Definition):请求发出后,无论模块是否存在,立即执行后续的逻辑,实现模块开发中对模块的立即引用。
// 1、模块管理器对象
var F = {};
// 2、定义模块的方法
F.define = function(str, fn) {
var parts = str.split('.');
var old, parent;
// old存储当前模块的祖父模块,parent存储当前模块父模块
old = parent = this;
// 如果第一个是模块是模块管理器对象,则移除
// F.string.trim => string.trim
if (parts[0] === 'F') {
parts = parts.slice(1);
}
// 屏蔽本身提供的define和module方法
if (parts[0] === 'define' || parts['0'] === 'module') {
return;
}
for(var i = 0, len = parts.length; i < len; i++) {
// 当前模块不存在父模块中
if (typeof parent[parts[i]] === 'undefined') {
parent[parts[i]] = {};
}
old = parent; // 缓存当前父模块为下一轮循环中的祖父模块
parent = parent[parts[i]]; // 缓存当前模块为下一轮循环中的父模块
// 如果str为‘sting.trim’ 第一次循环 F.string = {};
// 第二次循环 F.string.trim = {};
// 此时old为string这个对象,parents为trim这个对象
}
// 如果给定模块方法,则直接调用该模块方法
if (fn) {
// old为倒数第二个层级的模块
// parts[parts.length-1] 为最后一个层级的模块
old[parts[parts.length-1]] = fn();
}
return this;
};
// 3、定义模块
// 定义string模块,父模块不能晚于子模块定义,因为子模块定义时若没有父模块会新建一个,若晚于子模块定义,子模块定义声明的父模块会整个被覆盖
F.define('string', function() {
return {
call: function() {
console.log('string call')
}
}
})
// 继续定义string.trim方法
F.define('string.trim', function() {
return function(str) {
return str.replace(/^\s+|\s+$/g, '');
}
})
// 测试string模块
var demo = F.string.trim(' abc ');
console.log(demo); // 'abc'
F.string.call(); // 'string call'
// 定义dom模块
F.define('dom', function() {
var $ = function(id) {
$.dom = document.getElementById(id);
return $;
}
$.html = function(html) {
// 有参数代表setter,没参数代表getter
if (html) {
this.dom.innerHTML = html;
} else {
return this.dom.innerHTML;
}
}
return $;
})
// 测试dom模块
// 测试页面 <div id="test">test</div>
F.dom('test').html(); // 'test'
// 4、调用模块方法 => F.module(['string', 'dom'], () => { ... })
F.module = function() {
var args = Array.from(arguments);
// 取最后一个参数作为回调执行函数
var fn = args.pop();
// 如果第一个参数是数组,则需要的依赖模块组为第一个参数,否则需要依赖的模块组为整个参数列表(除最后一个外)
var moduleArr = args[0] && args[0] instanceof Array ? args[0] : args;
// 需要依赖(调用)的模块列表
var modules = [];
// 遍历参数列表,找到所有需要依赖的模块
for (var i = 0, mLen = moduleArr.length; i < mLen; i++) {
// 举例:moduleArr为 ['string.trim', 'F.dom'];
if (typeof moduleArr[i] === 'string') {
// 记录当前模块的父模块为F
var parent = this;
// 获取当前模块的路由路径,解析路由,并屏蔽首层模块 F.
var moduleParts = moduleArr[i].replace(/^F\./, '').split('.');
// 举例:moduleParts 为 ['string', 'trim'] 和 ['dom']
for (var j = 0, mPartsLen = moduleParts.length; j < mPartsLen; j++) {
parent = parent[moduleParts[j]] || false;
// 第一轮 parent = this['string'] = F.string
// 第二轮 parent = F.string['trim']
}
modules.push(parent); // 将最终指向的模块添加到依赖的模块列表
} else {
modules.push(moduleArr[i]); // 如果不是路径,将该模块直接添加进列表
}
}
// 收集到所有需要调用的模块列表,回传给调用者
fn.apply(null, modules);
}
// 5、使用
// 将需要使用的一级模块依次传入
F.module('string', 'dom', function(string, dom) {
console.log(string.trim(' 123 ')); // 123
console.log(dom('test').html()); // test
})
// 将需要使用的一、二级模块一次传入
F.module('string.trim', 'dom', function(trim, dom) {
console.log(trim(' 456 ')); // 456
console.log(dom('test').html()); // test
})
// 将需要使用的模块用数组的方式传入
F.module(['string.trim', 'dom'], function(trim, dom) {
console.log(trim(' 456 ')); // 456
console.log(dom('test').html()); // test
})
// 自定义一个方法模块
var func1 = function(str) {
console.log('func1 module ' + 'params is ' + str)
}
F.module('string.trim', func1, function(trim, customFn) {
console.log(trim(' 456 ')); // 456
customFn('abc'); // abc
})
// 自定义一个对象模块
var obj = { name: 'jack', age: ' 18 '}
F.module('string.trim', obj, function(trim, customObj) {
console.log(trim(customObj.age)); // 18
console.log(customObj.name); // jack
})