在平常的业务中,值比较不仅仅是“==”或“===”,而是具有业务意义的值比较,比如查询条件的比较,数组值比较和业务日期的值比较等等。在编译语言中,例如java,可以重写hashcode和equal方法来进行引用类型比较,而基本类型比较则通过“==”来进行比较,那么js中是否可以这么做?当然可以,前提是在Object.prototype中添加hashcode和equal方法,但这种方式存在对象属性引入,容易造成未知属性污染。所以比较器组件Comparator.js,在进行组件式开发中,就尤其有用。
var class2type = {}, toString = class2type.toString,
map2type = {
"string": ["boolean", "number", "date", "regexp"],
"number": ["string", "boolean"],
"array": "object",
"date": "string",
"regexp": "string"
};
//遍历数组元素进行处理.
function each(args, cb) {
var i = 0, len = args.length;
for(; i < len; i++) {
cb(i, args[i]);
}
}
//两个类型比较.
function _compare(src, target) {
if(src === target) return true;
var srcType = class2type[toString.call(src)], targetType = class2type[toString.call(target)];
//类型相同时
if(srcType === targetType) {
if(srcType === "string" || srcType === "number" || srcType === "boolean") {
return src === target;
} else if(srcType === "object") {
return this.compareObject(src, target);
} else if(srcType === "array") {
return this.compareArray(src, target);
} else if(srcType === "date") {
return this.compareDate(src, target);
} else if(srcType === "regexp") {
return this.compareRegExp(src, target);
}
//类型可以比较时
} else if(this.canComparable(srcType, targetType)) {
if(srcType === "string") {
if(targetType === "boolean") {
return this.compareBoolean(src, target);
} else if(targetType === "number") {
return src === String(target);
} else if(targetType === "date") {
return this.compareDate(src, target);
} else if(targetType === "regexp") {
return this.compareRegExp(src, target);
}
} else if(srcType === "number") {
return this.compareNumber(Number(src), Number(target));
} else if(srcType === "array") {
return this.compareArray(src, target);
} else if(srcType === "date") {
return this.compareDate(src, target);
} else if(srcType === "regexp") {
return this.compareRegExp(src, target);
}
}
return false;
}
//时间比较, 只能比较到日.
function _compareDate(dSrc, dTarget) {
var srcType = class2type[toString.call(dSrc)],
targetType = class2type[toString.call(dTarget)];
if(srcType === targetType) {
return _dateFormat(dSrc, "yyyy-MM-dd") === _dateFormat(dTarget, "yyyy-MM-dd");
} else if(srcType === "date") {
return _dateFormat(dSrc, "yyyy-MM-dd") === dTarget;
} else if(srcType === "string") {
return dSrc === _dateFormat(dTarget, "yyyy-MM-dd");
}
}
//格式化日期显示.
function _dateFormat(date, fmtStr) {
if(!fmtStr || typeof fmtStr != "string") return date;
var fmt = { //按字母分开
"M+": date.getMonth() + 1, //月
"d+": date.getDate(), //日
"h+": date.getHours(), //时
"m+": date.getMinutes(), //分
"s+": date.getSeconds(), //秒
"q+": Math.floor(date.getMonth() + 3) / 3, //季度
"S": date.getMilliseconds() //毫秒
};
date = typeof date === "string" && new Date(+String(date.match(/\d+/))) ||
typeof date === "number" && new Date(date) || new Date();
fmtStr = /(y+)/.test(fmtStr) && fmtStr.replace(RegExp.$1,
(date.getFullYear() + "").substr(4 - RegExp.$1.length)) || fmtStr;
for(var k in fmt) {
if(new RegExp("(" + k + ")").test(fmtStr)) {
fmtStr = fmtStr.replace(RegExp.$1, RegExp.$1.length == 1 ? fmt[k] :
("00" + fmt[k]).substr(("" + fmt[k]).length));
}
}
return fmtStr;
}
//正则表达式比较.
function _compareRegExp(eSrc, eTarget) {
var srcType = class2type[toString.call(eSrc)],
targetType = class2type[toString.call(eTarget)];
if(srcType === targetType) {
return eSrc.source === eTarget.source;
} else if(srcType === "string") {
return eSrc === eTarget.source;
} else if(srcType === "regexp") {
return eSrc.source === eTarget;
}
}
//布尔类型值比较.
function _compareBoolean(iSrc, iTarget) {
var srcType = class2type[toString.call(iSrc)],
targetType = class2type[toString.call(iTarget)];
if(srcType === "string") {
iSrc = iSrc.replace(/\s+/g, "");
}
if(iTarget === "string") {
iTarget = iTarget.replace(/\s+/g, "");
}
return Boolean(iSrc) === Boolean(iTarget);
}
//两个数组进行比较.
function _compareArray(srcArr, targetArr) {
if(srcArr.length != targetArr.length) {
return false;
}
if(srcArr.sort) {
srcArr.sort();
}
if(targetArr.sort) {
targetArr.sort();
}
for(var i = 0, len = srcArr.length; i < len; i++) {
if(srcArr[i] != targetArr[i] && !this.compare(srcObj[name], targetObj[name])) {
return false;
}
}
return true;
}
//两个对象进行值比较.
function _compareObject(srcObj, targetObj) {
var srcProps = [], targetProps = [], name = null;
for(name in srcObj) srcProps.push(name);
for(name in targetObj) targetProps.push(name);
if(srcProps.length != targetProps.length) {
return false;
}
for(name in srcObj) {
if(srcObj[name] != targetObj[name] && !this.compare(srcObj[name], targetObj[name])) {
return false;
}
}
return true;
}
//两个类型是否可以比较.
function _canComparable(sType, tType) {
return map2type[sType].indexOf(tType) > -1;
}
//两个数字进行比较, 这里设置精度为8位.
function _compareNumber(srcNum, targetNum) {
return Number(srcNum).toFixed(8) === Number(targetNum).toFixed(8);
}
//类型添加.
each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
//定义比较器.
var Comparator = {
compare: _compare,
compareArray: _compareArray,
compareBoolean: _compareBoolean,
compareDate: _compareDate,
compareRegExp: _compareRegExp,
compareObject: _compareObject,
compareNumber: _compareNumber,
canComparable: _canComparable,
getInstance: function() {
var comparator = {};
for(var name in this) {
if(name !== "getInstance" && this[name]) {
comparator[name] = this[name];
}
}
return comparator;
}
};
//return Comparator;
<html>
<script type="text/javascript" src="./Comparator.js">script>
<script>
var obj1 = {name: "a", age: 17}, obj2 = {name: "a", age: 18};
//alert(Comparator.compare(obj1, obj2));
var obj3 = {name: "1", age: 2, person: obj1}, obj4 = {name: "1", age: 2, person: obj2};
alert(Comparator.compare(obj3, obj4));
var date1 = "2017-09-24", date2 = new Date();
//alert(Comparator.compare(date1, date2));
var arr1 = [1,2,3,4,5], arr2 = [1,3,4,2,5];
//alert(Comparator.compare(arr1, arr2));
script>
html>
对js中的基本类型进行值比较,而不是简单的“==”和“===”在业务应用中十分常见,js相对与java来说,缺少hashcode和equal方法,那么以js组件进行api的抽取,来达到通用的值比较则具有一定的实践意义。