分享自己的Javascript交互模板
在两年的工作中,经常会开发新的Web页面交互,或维护已存在的Web页面交互程序:javascript。由于 Javascript 是弱类型语言,且语言本身的约束性不是很强。Coding时如果没有统一的约束或规范,将会造成 .js 文件排版混乱不堪很难维护。
现如今jQuery已经成为Web页面交互中不可缺少的组成部分,下面分享一套我在工作中累计的(个人认为)还不错的 .js 文件模板。
- 解决通过$.load(或 $.html 等其他工具)加载的页面中包含 .js 文件时, 无法在部分浏览器定位并调试的问题。
// 调试开关, 页面初始化有效
FunctionFn.debug = true;
// 打印 .js 文件名
FunctionFn.debug && window.console && console.log("fileName.js");- 构建 Javascript 程序统一入口(jQuery入口)
// 页面加载完成后,
// 在 onload 事件中注册交互事件和初始化页面数据
$(function() {
// 将唯一实例绑定到函数(类)自身为了方便实时获取运算数据
TestFn.instance = new TestFn();
TestFn.instance.initEvents().initData();
});- 约定交互程序统一对外函数(initEvents 和 initData)
// 交互主体函数
function TestFn() {
// 由于 this 会跟随上下文环境不同而变化
// 在此记录交互实例对象, 以便于在需要时不必非常麻烦的通过 arguments.callee.caller 等方式获取上下文环境
var $thisObj = this;
// 交互主体所能控制的最顶层容器控件
// 在后续查找控件时, 应从 baseCtnr 中开始查找: baseCtnr.find($ctrlSelector);
// 而不是索引全局: $($ctrlSelector);
// Base Container
var baseCtnr = $(".current-base-container");
// 1. 没有通过 new 关键字获取对象时, 返回执行环境对象
// 2. 使用 new 关键字获取对象时, 返回 TestFn 的实例对象
// 无论通过何种方式获取对象, 都可以访问绑定到 $thisObj 的函数和数据
// 所以在此返回 this 是很有必要的
return this;
}- 约定常量值集合(FINAL_VALUE)
// 用于保存运算过程中不能被变更的值, 实现时应使用 getter 函数方式. 如:
// var finalPageSize = FINAL_VALUE.VALUES.getFinalPageSize();
// 而不是直接定义数据属性: FINAL_VALUE.VALUES.FINAL_PAGE_SIZE = 10;
// 这种方式很容易被无意识的修改
// 常量值字典对象
var FINAL_VALUE = {
KEYS: {
CONFIG_KEYS: {
_summary: "本地配置字典"
},
SELECTORS: {
_summary: "jQuery选择器字典"
},
ATTRIBUTES: {
_summary: "控件属性字典"
},
_summary: "关键字字典 "
},
VALUES: {
_summary: "常量值字典"
},
_summary: "常量值字典. 使用getter实现, 如: getFinalPageSize"
};- 约定数据对象(data)
// 数据集合
// 访问数据应使用 dataManager 对象
var data = {
service: {
_summary: "业务数据对象, 保存需要提交的表单数据"
},
cached: {
_summary: "用于UI计算的缓存数据对象"
},
temporaries: {
_summary: "临时数据集合"
},
_summary: "数据集合"
};- 约定数据维护对象(dataManager)
// 数据维护对象
var dataManager = {
_summary: "数据维护对象, 提供 data 集合中数据计算能力"
};- 约定UI绘制程序集(UIPrinters)
// UI绘制对象
// HTML 控件/控件属性查找/编辑, 页面重绘等应统一使用该对象
var UIPrinters = {
_summary: "UI界面绘制集合. 提供HTML控件查找, 编辑等能力; 如 getSearchConditionFormContainer, getCurrentPageNumber 等等"
};- 约定配置(业务)程序集(config)
// 配置(业务)集合
// 此对象配置业务模块化运算能力, 以及事件管理基础功能
var config = {
serviceModules: {
// FIXME example start
download: {
beforeValidPermission: function(callback) {
var hasPermission = true;
// Remote validation: requestUtil.jsonAjax();
!!hasPermission && callback();
},
getChoseData: function() {
// 调用前确保 dataManager.getCurrPageChoseData 已定义
return dataManager.getCurrPageChoseData();
},
sendRequest: function(choseDataKeys) {
// 调用前确保 UIPrinters.loadingWin 已定义
UIPrinters.loadingWin.loading();
// requestUtil.download(choseDataKeys);
UIPrinters.loadingWin.completed();
},
invoke: function() {
// downloadCtt -> Download Context
var downloadCtt = config.events.download;
downloadCtt.beforeValidPermission(function() {
var data = downloadCtt.getChoseData();
downloadCtt.sendRequest(data);
});
}, // FIXME example end
_summary: "下载模块, 示例模块"
},
_summary: "业务集合. Key-模块名; Value:{Key:业务名, Value:处理函数}. 在每个模块中应定义_summary用以说明该模块的用途"
},
eventsManager: {
events: {
_summary: "事件列表, 为initRegister提供支撑"
},
initRegister: {
delegate: function() {
// TODO Register delegate events
// 注册委托事件
// 选择器使用 KEYS.SELECTORS, 如:
// $(KEYS.SELECTORS.getSearchFormContainerSelector)
},
singleUse: function() {
// TODO Register single-use events
// 注册一次性事件
// 选择器使用 KEYS.SELECTORS, 如:
// $(KEYS.SELECTORS.getSearchFormContainerSelector)
}
},
bind: {
singleUse: function(item, eventType, eventFn) {
},
delegate: function(item, eventType, eventFn) {
},
_summary: "事件绑定"
},
unbind: {
singleUse: function(item) {
},
delegate: function(item) {
},
_summary: "事件卸载, 如果删除元素时使用$el.remove(), 会自动卸载事件. 委托事件(delegate)需要根据实际情况考虑是否需要卸载"
},
_summary: "事件管理器, 用于支撑控件的事件绑定和卸载"
},
_summary: "本地配置实现, 提供业务逻辑支撑而不是实现"
};- 约定控制台(和/或发送到Server)的日志规范
// 独立的日志函数
// 善于利用浏览器提供的控制台(window.console)工具, 在很大程度上可以帮助 coder 们快速定位并解决现有问题
// 当前日志程序打印格式: [local-file:row-number] [caller|anonymous] [date] - message
// 打印格式可根据项目需求自行调整, 强烈建议保留 [local-file:row-number], 此信息能快速定位 .js 文件中的位置, 对于 Fix issue 非常有用. 此功能是我特意在 Google 中查找并加入的功能
function log(msg) {
if ($thisObj.debug) {
var formattedNow = new Date().format("yyyy-MM-dd hh:mm:ss");
var caller = arguments.callee.caller.name;
caller = caller || "Anonymous";
var local = "__LOCATION__";
var logPrefix = "[" + local + "] [" + caller + "] [" + formattedNow + "] - ";
if (window.console) {
try {
throw new Error();
} catch (e) {
var callerInfo = e.stack.match(/\n.*/g)[1];
callerInfo = callerInfo.match(/http:+.*[\d]/)[0];
// callerInfo = callerInfo.replace(stringUtil.getBaseUrl(),
// FINAL_VALUE.EMPTY_STRING);
logPrefix = logPrefix.replace(local, callerInfo);
}
console.log(logPrefix + JSON.stringify(msg));
(msg instanceof Object) && console.log(msg);
}
}
}满足上述约定后,Javascript 程序模板预览如下。
TestFn.debug = true;
TestFn.debug && window.console && console.log("test.js");
$(function() {
TestFn.instance = new TestFn();
TestFn.instance.initEvents().initData();
});
/**
* @description test.jsp 页面交互
* @author
* @version v1.0
* @date
*/
function TestFn() {
var $thisObj = this;
var baseCtnr = $(".test-body-container:eq(0)");
/** 常量值集合 */
var FINAL_VALUE = {
KEYS: {
CONFIG_KEYS: {
_summary: "本地配置字典"
},
SELECTORS: {
_summary: "jQuery选择器字典"
},
ATTRIBUTES: {
_summary: "控件属性字典"
},
_summary: "关键字字典 "
},
VALUES: {
_summary: "常量值字典"
},
_summary: "常量值字典. 使用getter实现, 如: getFinalPageSize"
};
/** 数据集合, 访问数据应使用 dataManager 对象 */
var data = {
service: {
_summary: "业务数据对象, 保存需要提交的表单数据"
},
cached: {
_summary: "用于UI计算的缓存数据对象"
},
temporaries: {
_summary: "临时数据集合"
},
_summary: "数据集合"
};
/** 数据维护对象 */
var dataManager = {
_summary: "数据维护对象, 提供 data 集合中数据计算能力"
};
/** UI绘制对象 */
var UIPrinters = {
_summary: "UI界面绘制集合. 提供HTML控件查找, 编辑等能力; 如 getSearchConditionFormContainer, getCurrentPageNumber 等等"
};
/** 配置(业务)集合 */
var config = {
serviceModules: {
// FIXME example start
download: {
beforeValidPermission: function(callback) {
var hasPermission = true;
// Remote validation: requestUtil.jsonAjax();
!!hasPermission && callback();
},
getChoseData: function() {
// 调用前确保 dataManager.getCurrPageChoseData 已定义
return dataManager.getCurrPageChoseData();
},
sendRequest: function(choseDataKeys) {
// 调用前确保 UIPrinters.loadingWin 已定义
UIPrinters.loadingWin.loading();
// requestUtil.download(choseDataKeys);
UIPrinters.loadingWin.completed();
},
invoke: function() {
// downloadCtt -> Download Context
var downloadCtt = config.events.download;
downloadCtt.beforeValidPermission(function() {
var data = downloadCtt.getChoseData();
downloadCtt.sendRequest(data);
});
}, // FIXME example end
_summary: "下载模块, 示例模块"
},
_summary: "业务集合. Key-模块名; Value:{Key:业务名, Value:处理函数}. 在每个模块中应定义_summary用以说明该模块的用途"
},
eventsManager: {
events: {
_summary: "事件列表, 为initRegister提供支撑"
},
initRegister: {
delegate: function() {
// TODO Register delegate events
// 注册委托事件
// 选择器使用 KEYS.SELECTORS, 如:
// $(KEYS.SELECTORS.getSearchFormContainerSelector)
},
singleUse: function() {
// TODO Register single-use events
// 注册一次性事件
// 选择器使用 KEYS.SELECTORS, 如:
// $(KEYS.SELECTORS.getSearchFormContainerSelector)
}
},
bind: {
singleUse: function(item, eventType, eventFn) {
},
delegate: function(item, eventType, eventFn) {
},
_summary: "事件绑定"
},
unbind: {
singleUse: function(item) {
},
delegate: function(item) {
},
_summary: "事件卸载, 如果删除元素时使用$el.remove(), 会自动卸载事件. 委托事件(delegate)需要根据实际情况考虑是否需要卸载"
},
_summary: "事件管理器, 用于支撑控件的事件绑定和卸载"
},
_summary: "本地配置实现, 提供业务逻辑支撑而不是实现"
};
/** 初始化事件 */
this.initEvents = function() {
try {
config.eventsManager.initRegister.singleUse();
config.eventsManager.initRegister.delegate();
} catch (e) {
log(e.message);
}
return $thisObj;
};
/** 初始化数据 */
this.initData = function() {
try {
// 这里只定义函数的调用顺序, 而不应该直接在此编写实现代码
} catch (e) {
log(e.message);
}
return $thisObj;
};
// 日志记录
function log(msg) {
if ($thisObj.debug) {
var formattedNow = new Date().format("yyyy-MM-dd hh:mm:ss");
var caller = arguments.callee.caller.name;
caller = caller || "Anonymous";
var local = "__LOCATION__";
var logPrefix = "[" + local + "] [" + caller + "] [" + formattedNow + "] - ";
if (window.console) {
try {
throw new Error();
} catch (e) {
var callerInfo = e.stack.match(/\n.*/g)[1];
callerInfo = callerInfo.match(/http:+.*[\d]/)[0];
// callerInfo = callerInfo.replace(stringUtil.getBaseUrl(),
// FINAL_VALUE.EMPTY_STRING);
logPrefix = logPrefix.replace(local, callerInfo);
}
console.log(logPrefix + JSON.stringify(msg));
(msg instanceof Object) && console.log(msg);
}
}
}
return this;
}为什么写此文章:
- 记录并分享工作总结也是一件乐事。如果能帮助别人岂不快哉^^.
- 养成写博文的习惯有助于总结工作中的有点, 找到自己的不足. 通常喜欢写博文的童鞋也会经常看别人的博文。记得在哪本书上(忘记是Code2还是Javascript高级编程
)看到一句话: 读书就是把别人的经历变成自己的经验。
eclipse模板下载:
Google javascript 格式化模板:google-js-formatter.xml
上面提到的 Javascript 代码模板:yong-defined-js-templates.xml
如何使用
- 导入模板到eclipse中(如何导入? 童鞋, 这么简单的问题不可能不会吧
) - 在编辑窗口中数据: $pagefn, $pagefn, $this, $thisobj, 激活上下文提示(alt + /)