重构前的话
上一篇发布一个JavaScript工具类库jutil,欢迎使用,欢迎补充,欢迎挑错!发布后有幸得到了大部分朋友的肯定,在这里多谢各位的支持鼓励。
在上一篇评论中,也有园友指出了存在的问题,主要有建议增加数组过滤方法,HTMLEncode和HTMLDecode方法不安全,数组去重方法的问题等,没有命名空间,扩展不太方便。本次重构主要解决的就是上面这些问题。
本次重构具体的就是增加了数组过滤方法,划分了一下命名空间,增加了用于扩展的方法。对于HTMLEncode和HTMLDecode方法不安全的问题,本次重构没有处理,如果需要安全的HTMLEncode和HTMLDecode建议使用http://www.strictly-software.com/scripts/downloads/encoder.js这个JS库。数组去重的问题我测试了一下,当数组中存在true、false等bool值时会出问题,因此改为园友(jamcode)提供的方法。
如何给类库添加命名空间
让类库拥有命名空间的方法很多,我这里用的是:
jutil.html = jutil.html ? jutil.html : {//code}
这种方式,这里html就是二级命名空间。如之前的HTML相关的两个方法可以写成这样:
(function () { var document = window.document; var jutil = jutil ? jutil : { }; jutil.html = jutil.html ? jutil.html : { encode: function (sHtml) { var div = document.createElement("div"), text = document.createTextNode(sHtml); div.appendChild(text); return div.innerHTML; }, decode: function (sHtml) { var div = document.createElement("div"); div.innerHTML = sHtml; return div.innerText || div.textContent; } }; if (!window.jutil) { window.jutil = jutil; } })();
本次重构就采用了这种方法。
如何让类库方便扩展
这个直接参考了kit.js中的方案,就是提供了两个用于扩展的方法:
merge : function() { var a = arguments; if(a.length < 2) { return; } if(a[0] != null) { for(var i = 1; i < a.length; i++) { for(var r in a[i]) { a[0][r] = a[i][r]; } } } return a[0]; }, mergeIfNotExist : function() { var a = arguments; if(a.length < 2) { return; } for(var i = 1; i < a.length; i++) { for(var r in a[i]) { if(a[0][r] == null) { a[0][r] = a[i][r]; } } } return a[0]; }
这两个方法都比较简单,就不多解释了。
重构后jutil代码
(function () { var document = window.document; var jutil = jutil ? jutil : { merge : function() { var a = arguments; if(a.length < 2) { return; } if(a[0] != null) { for(var i = 1; i < a.length; i++) { for(var r in a[i]) { a[0][r] = a[i][r]; } } } return a[0]; }, mergeIfNotExist : function() { var a = arguments; if(a.length < 2) { return; } for(var i = 1; i < a.length; i++) { for(var r in a[i]) { if(a[0][r] == null) { a[0][r] = a[i][r]; } } } return a[0]; } }; jutil.array = jutil.array ? jutil.array : { distinct: function unique(arr) { var i = 0, gid = '_' + (+new Date) + Math.random(), objs = [], hash = { 'string': {}, 'boolean': {}, 'number': {} }, p, l = arr.length, ret = []; for (; i < l; i++) { p = arr[i]; if (p == null) continue; tp = typeof p; if (tp in hash) { if (!(p in hash[tp])) { hash[tp][p] = 1; ret.push(p); } } else { if (p[gid]) continue; p[gid] = 1; objs.push(p); ret.push(p); } } for (i = 0, l = objs.length; i < l; i++) { p = objs[i]; p[gid] = undefined; delete p[gid]; } return ret; }, indexOf: function (arr, obj, iStart) { if (Array.prototype.indexOf) { return arr.indexOf(obj, (iStart || 0)); } else { for (var i = (iStart || 0), j = arr.length; i < j; i++) { if (arr[i] === obj) { return i; } } return -1; } }, filter: function (arr, callback) { var result = []; for (var i = 0, j = arr.length; i < j; i++) { if (callback.call(arr[i], i, arr[i])) { result.push(arr[i]); } } return result; } }; jutil.html = jutil.html ? jutil.html : { encode: function (sHtml) { var div = document.createElement("div"), text = document.createTextNode(sHtml); div.appendChild(text); return div.innerHTML; }, decode: function (sHtml) { var div = document.createElement("div"); div.innerHTML = sHtml; return div.innerText || div.textContent; } }; jutil.storage = jutil.storage ? jutil.storage : { getCookie: function (sKey) { if (!sKey) return ""; if (document.cookie.length > 0) { var startIndex = document.cookie.indexOf(sKey + "=") if (startIndex != -1) { startIndex = startIndex + sKey.length + 1 var endIndex = document.cookie.indexOf(";", startIndex) if (endIndex == -1) { endIndex = document.cookie.length; } return decodeURIComponent(document.cookie.substring(startIndex, endIndex)); } } return "" }, setCookie: function (sKey, sValue, iExpireSeconds) { if (!sKey) return; var expireDate = new Date(); expireDate.setTime(expireDate.getTime() + iExpireSeconds * 1000); document.cookie = sKey + "=" + encodeURIComponent(sValue) + ";expires=" + expireDate.toGMTString() + ";"; }, deleteCookie: function (sKey) { if (!sKey) return; document.cookie = sKey + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; }, getStorage: function (sKey) { if (!sKey) return; if (window.localStorage) { return decodeURIComponent(localStorage.getItem(sKey)); } else { return this.getCookie(sKey); } }, setStorage: function (sKey, sValue, iExpireSeconds) { if (!sKey) return; if (window.localStorage) { localStorage.setItem(sKey, encodeURIComponent(sValue)); } else { this.setCookie(sKey, sValue, iExpireSeconds); } }, deleteStorage: function (sKey) { if (!sKey) return; if (window.localStorage) { localStorage.removeItem(sKey); } else { this.deleteCookie(sKey); } } }; jutil.date = jutil.date ? jutil.date : { daysInFebruary: function (obj) { var year = 0; if (obj instanceof Date) { year = obj.getFullYear(); } else if (typeof obj === "number") { year = obj; } else { return 0; } if (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)) { return 29; } return 28; }, daysInYear: function (obj) { var year = 0; if (obj instanceof Date) { year = obj.getFullYear(); } else if (typeof obj === "number") { year = obj; } else { return 0; } if (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)) { return 366; } return 365; }, format: function (date, sFormat, sLanguage) { var time = {}; time.Year = date.getFullYear(); time.TYear = ("" + time.Year).substr(2); time.Month = date.getMonth() + 1; time.TMonth = time.Month < 10 ? "0" + time.Month : time.Month; time.Day = date.getDate(); time.TDay = time.Day < 10 ? "0" + time.Day : time.Day; time.Hour = date.getHours(); time.THour = time.Hour < 10 ? "0" + time.Hour : time.Hour; time.hour = time.Hour < 13 ? time.Hour : time.Hour - 12; time.Thour = time.hour < 10 ? "0" + time.hour : time.hour; time.Minute = date.getMinutes(); time.TMinute = time.Minute < 10 ? "0" + time.Minute : time.Minute; time.Second = date.getSeconds(); time.TSecond = time.Second < 10 ? "0" + time.Second : time.Second; time.Millisecond = date.getMilliseconds(); time.Week = date.getDay(); var MMMArrEn = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], MMMArr = ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], WeekArrEn = ["Sun", "Mon", "Tue", "Web", "Thu", "Fri", "Sat"], WeekArr = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"], oNumber = time.Millisecond / 1000; if (sFormat != undefined && sFormat.replace(/\s/g, "").length > 0) { if (sLanguage != undefined && sLanguage === "en") { MMMArr = MMMArrEn.slice(0); WeekArr = WeekArrEn.slice(0); } sFormat = sFormat.replace(/yyyy/ig, time.Year) .replace(/yyy/ig, time.Year) .replace(/yy/ig, time.TYear) .replace(/y/ig, time.TYear) .replace(/MMM/g, MMMArr[time.Month - 1]) .replace(/MM/g, time.TMonth) .replace(/M/g, time.Month) .replace(/dd/ig, time.TDay) .replace(/d/ig, time.Day) .replace(/HH/g, time.THour) .replace(/H/g, time.Hour) .replace(/hh/g, time.Thour) .replace(/h/g, time.hour) .replace(/mm/g, time.TMinute) .replace(/m/g, time.Minute) .replace(/ss/ig, time.TSecond) .replace(/s/ig, time.Second) .replace(/fff/ig, time.Millisecond) .replace(/ff/ig, oNumber.toFixed(2) * 100) .replace(/f/ig, oNumber.toFixed(1) * 10) .replace(/EEE/g, WeekArr[time.Week]); } else { sFormat = time.Year + "-" + time.Month + "-" + time.Day + " " + time.Hour + ":" + time.Minute + ":" + time.Second; } return sFormat; }, diff: function (biggerDate, smallerDate) { var intervalSeconds = parseInt((biggerDate - smallerDate) / 1000); if (intervalSeconds < 60) { return intervalSeconds + "秒"; } else if (intervalSeconds < 60 * 60) { return Math.floor(intervalSeconds / 60) + "分钟"; } else if (intervalSeconds < 60 * 60 * 24) { return Math.floor(intervalSeconds / (60 * 60)) + "小时"; } else if (intervalSeconds < 60 * 60 * 24 * 7) { return Math.floor(intervalSeconds / (60 * 60 * 24)) + "天"; } else if (intervalSeconds < 60 * 60 * 24 * 31) { return Math.floor(intervalSeconds / (60 * 60 * 24 * 7)) + "周"; } else if (intervalSeconds < 60 * 60 * 24 * 365) { return Math.floor(intervalSeconds / (60 * 60 * 24 * 30)) + "月"; } else if (intervalSeconds < 60 * 60 * 24 * 365 * 1000) { return Math.floor(intervalSeconds / (60 * 60 * 24 * 365)) + "年"; } else { return Math.floor(intervalSeconds / (60 * 60 * 24)) + "天"; } }, interval: function (biggerDate, smallerDate) { var intervalSeconds = parseInt((biggerDate - smallerDate) / 1000), day = Math.floor(intervalSeconds / (60 * 60 * 24)), hour = Math.floor((intervalSeconds - day * 24 * 60 * 60) / 3600), minute = Math.floor((intervalSeconds - day * 24 * 60 * 60 - hour * 3600) / 60), second = Math.floor(intervalSeconds - day * 24 * 60 * 60 - hour * 3600 - minute * 60); return day + "天:" + hour + "小时:" + minute + "分钟:" + second + "秒"; } }; jutil.string = jutil.string ? jutil.string : { replaceURLWithHTMLLinks: function (sText, bBlank) { var pattern = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; if (bBlank) { sText = sText.replace(pattern, "<a target='_blank' href='$1'>$1</a>"); } else { sText = sText.replace(pattern, "<a href='$1'>$1</a>"); } return sText; }, getLength: function (sVal, bChineseDouble) { var chineseRegex = /[\u4e00-\u9fa5]/g; if (bChineseDouble != undefined && bChineseDouble === false) { return sVal.length; } else { if (chineseRegex.test(sVal)) { return sVal.replace(chineseRegex, "zz").length; } return sVal.length; } } }; if (!window.jutil) { window.jutil = jutil; } })();
其中的merge和mergeIfNotExist方法参考了园子里JS牛人薛端阳的kit.js的设计。在这里也推荐一下kit.js,这是一个很优秀的JS库。
注意:本次重构由于加了命名空间,并且少部分方法名称有所更改,所以上一篇博文中的示例代码不能运行。
如何扩展
如何用上面增加的merge方法扩展呢,这里提供一个示例。这里我们增加一个去掉数组中值为空的项:
;(function(jutil){ jutil.array=jutil.array?jutil.array:{}; jutil.merge(jutil.array,{ deleteEmpty:function(arr){ for(var i=0,j=arr.length;i<j;){ if(arr[i]==""||arr[i]==null){ arr.splice(i,1); j=arr.lehgth; } else{ i++; } } return arr; } }); })(jutil);
个人感觉还是挺方便,挺简单的。
小结
这是我第一次真正动手写JS类库,真正体会到了看别人的类库跟自己写类库的区别。我想园子里肯定有很多像我这样的前端新手,想写类库却不知道从哪儿下手,但愿随着这个类库的不断改进和重构,也能给大家一点提示和帮助。
在此也真心希望各位有经验的朋友多多指正,分享你们的类库设计经验,指出本类库中的不足。