<body>
<h3>设计模式知识连载(45)---同步模块模式:</h3>
<div>
<p>
模块化:将复杂的系统分解成高内聚、低耦合的模块,使系统开发变得可控、可维护、可拓展,提高模块的复用率
</p>
<p>
请求发出后,无论模块是否存在,立即执行后续的逻辑,实现模块开发中对模块的立即引用。
</p>
</div>
<hr>
<div id = 'test'> test </div>
<script type="text/javascript">
/**
* 案例一:排队开发,方式一:初始
*/
// /* A工程师,获取数据并创建导航模块(沿用A框架)*/
// // 导航数据
// var data = null ;
// // 获取导航逻辑
// var dom = A('#nav') ;
// // 创建导航逻辑
// var createNav = function() {
// // A 工程师完成导航创建逻辑
// };
// /****** C工程师加入,为导航添加引导图片 ******/
// var lis = A('li', dom) ;
// for(var i = 0; i < data.length; i++) {
// if(data[i].hasGuide) {
// $(lis[i]).addClass('has-guide') ;
// }
// }
// /*********************************************/
// /****** B工程师加入,完成对导航添加事件需求 ******/
// // 在导航容器中获取每条导航容器
// var li = A('', dom) ;
// li.on('mouseover', function() {
// // 显示下拉狂逻辑
// }).on('mouseout', function() {
// // 隐藏下拉框逻辑
// }) ;
// /*************************************************/
// // 获取导航数据
// A.ajax('data/nav', function(res) {
// if(res.errNo == 0 ) {
// // 保存数据
// data = res.data ;
// // 创建的导航模块
// createNav() ;
// }
// }) ;
/**
* 案例一:排队开发,方式二:进阶
*/
// 定义模块管理器单体对象
var F = F || {} ;
/**
* 定义模块方法(理论上,模块方法应放在闭包中实现,可以隐蔽内部信息。【此处忽略此步骤】)
* @param str 模块路由
* @param fn 模块方法
**/
F.define = function(str, fn) {
// 解析模块路由
var parts = str.split('.') ;
// old当前模块的祖父模块,parent当前模块父模块
// 如果在闭包中,为了屏蔽对模块直接访问,建议将模块添加给闭包内部私有变量
var old = parent = this ;
// i:模块层级;len:模块层级长度
var i = len = 0 ;
// 如果第一个模式是模块管理器单体对象,则移除
if(parts[0] === 'F') {
// slice() 方法可从已有的数组中返回选定的元素。
parts = parts.slice(1) ;
}
// 屏蔽对define与module模块方法的重写
if(parts[0] === 'define' || parts[0] === 'module') {
return ;
}
// 遍历路由模块并定义每层模块
for(len = parts.length; i < len ; i++) {
// 如果父模块中不存在当前模块
if(typeof parent[parts[i]] === 'undefined') {
// 声明当前模块
parent[parts[i]] = {} ;
}
// 缓存下一层级的祖父模块
old = parent ;
// 缓存下一层级父模块
parent = parent[parts[i]] ;
}
// 如果给定模块方法则定义该模块方法
if(fn) {
// 此时i等于parts.length,故减一
old[parts[--i]] = fn() ;
}
// 返回模块管理器单体对象
return this ;
}
// 创建模块
// F.string模块
F.define('string', function() {
// 接口方法
return {
// 清除字符串两边空白
trim : function(str) {
return str.replace(/^\s+|\s+$/g, '') ;
}
}
}) ;
// 测试用例:
/*
注意:在真正的模块开发中,是不允许这样直接调用的,有两点原因,技术上,模块通常保存在闭包内部的私有变量里,而不会保存在F上,因此是获取不到的,而这里简化了闭包,也为测试方便,因此直接引用。其次,类似如下调用是不符合模块化开发规范的。
*/
var testStr = ' 测试用例 1234' ;
console.log('testStr.length:', testStr.length) ;
var str1 = F.string.trim(testStr) ;
console.log('str1.length:', str1.length) ;
/*******************************************************************/
/*
对于模块的回调函数,也可以以构造函数的形式返回接口,比如创建DOM模块,其中包括dom()获取元素方法、html()获取或者设置元素innerHTML内容方法等
*/
F.define('dom', function() {
// 简化获取元素方法(重复获取可被替代,此设计用于演示模块添加)
var $ = function(id) {
$.dom = document.getElementById(id) ;
// 返回构造函数对象
return $ ;
}
// 获取或者设置元素内容
$.html = function(html) {
// 如果传参则设置元素内容,否则获取元素内容
if(html) {
this.dom.innerHTML = html ;
return this ;
}else {
return this.dom.innerHTML ;
}
}
// 返回构造函数
return $ ;
}) ;
// 测试用例:
var text = F.dom('test').html() ;
console.log('text:', text) ;
/*******************************************************************/
/*
对于模块的创建,也可以先声明后创建,比如添加addClass()为元素添加class方法。
*/
// 为dom模块添加addClass方法
// 注意:此种添加模式之所以可行,是因为将模块添加到F对象上,模块化开发中只允许上面的添加方式
F.define('dom.addClass') ;
F.dom.addClass = (function(type, fn) {
return function(className) {
// 如果不存在该类
if(!~this.dom.className.indexOf(className)) {
// 简单添加类
this.dom.className += ' ' + className ;
}
}
})() ;
// 测试用例:
F.dom('test').addClass('my-class') ;
/*******************************************************************/
// 模块调用方法---创建一个“使用”模块方法:module
F.module = function() {
// 将参数转化为数组
var args = [].slice.call(arguments) ;
// 获取回调执行函数
var fn = args.pop() ;
// 获取依赖模块,如果args[0]是数组,则依赖模块args[0]。否则依赖模块arg
var parts = args[0] && args[0] instanceof Array ? args[0] : args ;
// 依赖模块列表
var modules = [] ;
// 模块路由
var modIDs = '' ;
// 依赖模块索引
var i = 0 ;
// 依赖模块长度
var ilen = parts.length ;
// 父模块,模块路由层级索引,模块路由层级长度
var parent, j, jlen ;
// 遍历依赖模块
while(i < ilen) {
// 如果是模块路由
if(typeof parts[i] === 'string') {
// 设置当前模块父对象(F)
parent = this ;
// 解析模块路由,并屏蔽掉模块父对象
modIDs = parts[i].replace(/^F./, '').split('.') ;
// 遍历模块路由层级
for(j = 0, jlen = modIDs.length; j < jlen; j++) {
// 重置父模块
parent = parent[modIDs[j]] || false ;
}
// 将模块添加到依赖模块列表中
modules.push(parent) ;
// 如果是模块对象
}else {
// 直接加入依赖模块列表中
modules.push(parts[i]) ;
}
// 取下一个依赖模块
i++ ;
}
// 执行回调执行函数
fn.apply(null, modules) ;
}
// 调用模块
// 引用dom模块与document对象(注意:依赖模块对象通常为已创建的模块对象)
// F.module(['dom', document], function(dom, doc) {
// // 通过dom模块设置元素内容
// dom('test').html('new add!') ;
// // 通过document设置body元素背景色
// doc.body.style.background = 'red' ;
// }) ;
// 依赖引用dom模块,string.trim方法
F.module('dom', 'string.trim', function(dom, trim) {
// 测试元素:<div id = 'test'> test </div>
// 获取元素内容
var html = dom('test').html() ;
// 去除字符串两边空白符
var str = trim(html) ;
console.log('*' + html + '*', '*' + str + '*') ;
}) ;
</script>
</body>