来自:《 设计模式》(张容铭),自己留作备用,勿转。
同步模块模式
test.html文件:
<!DOCTYPE html>
<html lang=\"en\">
<head>
< charset=\"UTF-8\">
< name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
< http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">
< >Document</ >
< src=\"smd.js\"></ >
</head>
<body>
<div id=\"test\">test</div>
</body>
</html>
smd.js文件:
var F = F||{};
//定义模块管理器
F.define = function (str, fn) {
var parts = str.split(\'.\'),
old = parent = this,
i = len = 0;
if (parts[0] === \'F\') {
parts = parts.slice();
}
if (parts[0] === \'define\' || parts[0] === \'modules\') {
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) {
old[parts[--i]] = fn();
}
return this;
}
// 创建模块
// F.string模块
F.define(\'string\', function() {
// 接口方法
return {
// 清除字符串两边空白
trim : function(str) {
return str.replace(/^\\s+|\\s+$/g, \'\') ;
}
}
}) ;
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 $ ;
}) ;
/*******************************************************************/
/*
对于模块的创建,也可以先声明后创建,比如添加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 ;
}
}
})() ;
/*******************************************************************/
//模块调用方法---创建一个“使用”模块方法: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) ;
}
console里的测试代码:
// 调用模块
// 引用dom模块与document对象(注意:依赖模块对象通常为已创建的模块对象)
F.module([\'dom\', document], function(dom, doc) {
// 通过dom模块设置元素内容
dom(\'test\').html(\'new add!\') ;
// 通过document设置body元素背景色
doc.body.style.background = \'red\' ;
}) ;
异步模块模式:
html文件:
<!DOCTYPE html>
<html lang=\"en\">
<head>
< charset=\"UTF-8\">
< name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
< http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">
< >Document</ >
< src=\"amd.js\"></ >
</head>
<body>
<button id=\"demo\">aaa</button>
<button id=\"bbb\">bbb</button>
</body>
</html>
amd.js文件:
/**
* 闭包环境
**/
// 向闭包中传入模块管理其对象F(~屏蔽雅素偶文件时,前面漏写;报错)
~(function(F) {
// 模块缓存器。存储已创建模块
var moduleCache = {} ;
}) ((function() {
// 创建模块管理器对象F,并保存在全局作用域中
return window.F = {} ;
}) ()) ;
/**
* 创建或调用模块方法
* @param url 参数为模块URL
* @param deps 参数为依赖模块
* @param callback 参数为模块主函数
**/
F.module = function(url, modDeps, modCallback) {
// 将参数转化为数组
var args = [].slice.call(arguments) ;
// 获取模块构造函数(参数数组中最后一个参数成员)
var callback = args.pop() ;
// 获取依赖模块(紧邻回调函数参数,且数据类型为数组)
var deps = (args.length && args[args.length - 1] instanceof Array) ? args.pop() : [] ;
// 该模块URL(模块ID)
var url = args.length ? args.pop() : null ;
// 依赖模块序列
var params = [] ;
// 未加载的依赖模块数量统计
var depsCount = 0 ;
// 依赖模块序列中索引值
var i = 0 ;
// 依赖模块序列长度
var len ;
// 获取依赖模块长度
if(len = deps.length) {
// 遍历依赖模块
while(i < len) {
// 闭包保存i
(function(i) {
// 增加未加载依赖模块数量统计
depsCount++ ;
// 异步加载依赖模块
loadModule(deps[i], function(mod) {
// 依赖模块序列中添加依赖模块接口引用
params[i] = mod ;
// 依赖模块加载完成,依赖模块数量统计减一
depsCount-- ;
// 如果依赖模块全部加载
if(depsCount === 0) {
// 在模块缓存器中矫正该模块,并执行构造函数
setModule(url, params, callback) ;
}
}) ;
}) (i) ;
// 遍历下一依赖模块
i++ ;
}
// 无依赖模块,直接执行回调函数
}else {
// 在模块缓存器中矫正该模块,并执行构造函数
setModule(url, [], callback) ;
}
}
// 加载模块
var moduleCache = {} ;
/***
* 异步加载依赖模块所在文件
* @param moduleName 模块路径(id)
* @param callback 模块加载完成回调函数
**/
var loadModule = function(moduleName, callback) {
// 依赖模块
var _module ;
// 如果依赖模块被要求加载过
if(moduleCache[moduleName]) {
// 获取该模块信息
_module = moduleCache[moduleName] ;
// 如果模块加载完成
if(_module.status === \'loaded\') {
// 执行模块加载完成回调函数
setTimeout(callback(_module.exports), 0) ;
}else {
// 缓存该模块所处文件加载完成回调函数
_module. .push(callback) ;
}
// 模块第一次被依赖引用
}else {
// 缓存该模块初始化信息
moduleCache[moduleName] = {
moduleName : moduleName, // 模块id
status : \'loading\', // 模块对应文件加载状态(默认加载中)
exports : null, // 模块接口
: [callback] // 模块对应文件加载完成回调函数缓冲器
};
// 加载模块对应文件
load (getUrl(moduleName)) ;
}
} ;
// 获取文件路径
var getUrl = function(moduleName) {
// 拼接完整的文件路径字符串,如\'lib/ajas\' => \'lib/ajax.js\'
return String(moduleName).replace(/\\.js$/g, \'\') + \'.js\' ;
} ;
// 加载脚本文件
var load = function(src) {
var _ = document.createElement(\' \') ;
// 文本类型
_ .type = \'text/ \' ;
// 确认编码
_ .charset = \'UTF-8\' ;
// 异步加载
_ .async = true ;
// 文件路径
_ .src = src ;
// 插入页面中
document.getElementsByTagName(\'head\')[0].appendChild(_ ) ;
};
/***
* 设置模块并执行模块构造函数
* @param moduleName 模块id名称
* @param params 依赖模块
* @param callback 模块构造函数
**/
var setModule = function(moduleName, params, callback) {
// 模块容器,模块文件加载完成回调函数
var _module, fn ;
// 如果模块被调用过
if(moduleCache[moduleName]) {
// 获取模块
_module = moduleCache[moduleName] ;
// 设置模块已经加载完成
_module.status = \'loaded\' ;
// 矫正模块接口
_module.exports = callback ? callback.apply(_module, params) : null ;
// 执行模块文件加载完成回调函数
while(fn = _module. .shift()) {
fn(_module.exports) ;
}
}else {
// 模块不存在(匿名函数),则直接执行构造函数
callback && callback.apply(null, params) ;
}
};
lib/dom.js文件
F.module(\'lib/dom\', function() {
return {
// 获取元素方法
g : function(id) {
return document.getElementById(id) ;
},
// 获取或者设置元素内容方法
html : function(id, html) {
if(html) {
this.g(id).innerHTML = html ;
}else {
return this.g(id).innerHTML ;
}
}
}
}) ;
lib/event.js文件
F.module(\'lib/event\', [\'lib/dom\'], function(dom) {
var events = {
// 绑定事件
on : function(id, type, fn) {
dom.g(id)[\'on\' + type] = fn ;
}
}
return events;
}) ;
Console里的测试代码:
F.module([\'lib/event\', \'lib/dom\'], function(events, dom) {
events.on(\'demo\', \'click\', function() {
dom.html(\'demo\', \'success\') ;
}) ;
}) ;
id为“demo”的button按钮被点击后,text变成了success。
继续阅读与本文标签相同的文章
下一篇 :
AliOS Things KV组件的写平衡特性
-
实时计算Flink on Kubernetes产品模式介绍
2026-05-18栏目: 教程
-
重磅|阿里云HBase Ganos全新升级,推空间、时空、遥感一体化基础云服务
2026-05-18栏目: 教程
-
Apache Flink 1.9.0版本新功能介绍
2026-05-18栏目: 教程
-
新功能初探 | RDS MySQL 8.0 支持 DML 语句 returning
2026-05-18栏目: 教程
-
浅谈分布式计算的开发与实现(二)
2026-05-18栏目: 教程
