var objectList = new StringBuilder();
var coll = document.getElementsByTagName("INPUT");
for (var i = 0; i < coll.length; i++)
{
var _obj = coll[i];
if (_obj.type != "checkbox") continue;
if (_obj.id.indexOf("functor_") >= 0 && _obj.checked == true)
{
var id = _obj.id;
id = id.substring("functor_".length, id.length);
functorList.append(id + ";");
}
}
for (var i = 0; i < coll.length; i++)
{
var _obj = coll[i];
if (_obj.type == "checkbox" && _obj.id.indexOf("object_") >= 0)
{
if (_obj.id.indexOf("_parent") > 0 || _obj.id.indexOf("_child") > 0) continue;
if (_obj.id.indexOf("_r") > 0 || _obj.id.indexOf("_a") > 0 || _obj.id.indexOf("_m") > 0 || _obj.id.indexOf("_d") > 0) continue;
var gid = pid.replace(/object_/g, "");
var orList = new StringBuilder();
orList.append(gid + ":");
for (var k = 0; k < coll.length; k++)
{
var _objK = coll[k];
if (_objK.type == "checkbox" && _objK.id == pid + "_r" && _objK.checked)
{
for (var r = 0; r < coll.length; r++)
{
var _objR = coll[r];
if (_objR.type == "radio" && _objR.getAttribute("name") == "radio_" + gid + "_r" && _objR.checked)
{
orList.append("r_" + _objR.value + ":");
break;
}
}
}
else if (_objK.type == "checkbox" && _objK.id == pid + "_a" & _objK.checked)
{
orList.append("a_" + ":");
}
else if (_objK.type == "checkbox" && _objK.id == pid + "_m" && _objK.checked)
{
for (var m = 0; m < coll.length; m++)
{
var _objM = coll[m];
if (_objM.type == "radio" && _objM.getAttribute("name") == "radio_" + gid + "_m" && _objM.checked)
{
orList.append("m_" + _objM.value + ":");
break;
}
}
}
else if (_objK.type == "checkbox" && _objK.id == pid + "_d" && _objK.checked)
{
for (var d = 0; d < coll.length; d++)
{
var _objD = coll[d];
if (_objD.type == "radio" && _objD.getAttribute("name") == "radio_" + gid + "_d" && _objD.checked)
{
orList.append("d_" + _objD.value + ":");
break;
}
}
}
}
objectList.append(orList.toString() + ";");
}
}
var objectListEx = new StringBuilder();
var coll = document.getElementsByTagName("INPUT");
var fnRE = /functor_[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}/i;
var objRE = /object_[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}$/i;
var objRE_r = /radio_[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}_r/i;
var objRE_a = /object_[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}_a/i;
var objRE_m = /radio_[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}_m/i;
var objRE_d = /radio_[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}_d/i;
var aryObjList = new Array();
var aryRList = new Array();
var aryAList = new Array();
var aryMList = new Array();
var aryDList = new Array();
// and intermediate arrays
for (var i = 0; i < coll.length; i++)
{
var _obj = coll[i];
if (_obj.type != "checkbox" && _obj.type != "radio") continue;
var _match;
if (_obj.checked && (_match = _obj.id.match(fnRE)) != null)
{
functorListEx.append(_match[0].split("_")[1] + ";");
}
else if (_obj.checked && (_match = _obj.name.match(objRE_r)) != null)
{
aryRList[_match[0].split("_")[1]] = "r_" + _obj.value;
}
else if (_obj.checked && (_match = _obj.id.match(objRE_a)) != null)
{
aryAList[_match[0].split("_")[1]] = "a_";
}
else if (_obj.checked && (_match = _obj.name.match(objRE_m)) != null)
{
aryMList[_match[0].split("_")[1]] = "m_" + _obj.value;
}
else if (_obj.checked && (_match = _obj.name.match(objRE_d)) != null)
{
aryDList[_match[0].split("_")[1]] = "d_" + obj.value;
}
else if (_obj.checked && (_match = _obj.id.match(objRE)) != null)
{
aryObjList.push(_match[0].split("_")[1]);
}
}
for (var i = 0; i < aryObjList.length; i++)
{
var _id = aryObjList[i];
var _r = aryRList[_id] == null ? "" : aryRList[_id];
var _a = aryAList[_id] == null ? "" : aryAList[_id];
var _m = aryMList[_id] == null ? "" : aryMList[_id];
var _d = aryDList[_id] == null ? "" : aryDList[_id];
objectListEx.append(_id + ":" + _r + ":" + _a + ":" + _m + ":" + _d + ";");
}
http://prowyh.spaces.live.com/blog/cns!EAAA8AB356F88EA0!403.entry
一、正则表达式的创建代码
---
这样的创建代码实在冗余:
var fnRE = /functor_[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}/i;
var objRE = /object_[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}$/i;
var objRE_r = /radio_[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}_r/i;
var objRE_a = /object_[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}_a/i;
var objRE_m = /radio_[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}_m/i;
var objRE_d = /radio_[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}_d/i;
仔细读来,其实就是一个添加前后缀的GUID。那么可否写成如下:
var GUID = '([0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12})';
var fnRE = new RegExp('(functor_)'+ GUID, 'i');
var objRE = new RegExp('(object_)' + GUID + '$', 'i');
var objRE_r = new RegExp('(radio_)' + GUID + '_(r)', 'i');
var objRE_a = new RegExp('(object_)' + GUID + '_(a)', 'i');
var objRE_m = new RegExp('(radio_)' + GUID + '_(m)', 'i');
var objRE_d = new RegExp('(radio_)' + GUID + '_(d)', 'i');
这里看起来是用了字符串连接运算,但由于变量声明只运算一次,因此对效率没什么影响。而且可读性强了,修改起来也方便。
读注意这里用到了正则表达式中的分组'( )',这在后面是会很有用的。
二、正则使用中的分组
---
代码总是通过
aryAList[_match[0].split("_")[1]] = "a_";
这样的形式来从匹配中分离GUID,但如果使用上面的分组,那么这项运算就不必要了。简单的使用
aryAList[_match[2]] = "a_";
就可以得到结果。
三、应注意DOM引用的耗时
---
代码中,在循环内不断地访问DOM对象的成员,然而DOM对象的成员存取是耗时的。更细的说,每一个成员
都会通过COM组件封装,因此效率是差的。所以下面的代码:
else if ((_match = _obj.name.match(objRE_m)) != null) {
}
else if ((_match = _obj.name.match(objRE_d)) != null) {
}
应当被改作:
var name = _obj.name;
else if ((_match = name.match(objRE_m)) != null) {
}
else if ((_match = name.match(objRE_d)) != null) {
}
四、过于复杂的逻辑
---
代码过于依赖其它语言的编程经验,而忽略了JavaScript的自身特性。因此实现的逻辑中规中矩,但是难以
扩展,而且复杂。例如循环中的大量if..else if ...连用。后文单独给出这部分的优化。
五、从StringBuilder()接口来看,优化程度不够
---
文章提到StringBuilder是一个字符串构建的高效对象。我留意到它的使用是:
objectListEx.append(_id + ":" + _r + ":" + _a + ":" + _m + ":" + _d + ";");
那么可以说这个对象的优化是不够的。因为这里传入一个字符串参数,而传入参数又用字符串连接运算,
效率提升很有限。
如果StringBuilder是用array.join来实现字符串拼接的话,那么更加良好的方式是允许在append中使用多
个参数。例如:
objectListEx.append = function() {
this.push.apply(this, arguments);
}
objectListEx.toString = function() {
return this.join('');
}
那么,上例的添加就可以写成:
objectListEx.append(_id , ":" , _r , ":" , _a , ":" , _m , ":" , _d , ";");
这就避免了多余的字符串连接运算。
六、新的优化后版本
---
// optimized version
var functorListEx = new StringBuilder();
var objectListEx = new StringBuilder();
var coll = document.getElementsByTagName("INPUT");
// regular expression for matching
var GUID = '([0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12})';
var fnRE = new RegExp('(functor_)'+ GUID, 'i');
var objRE = new RegExp('(object_)' + GUID + '$', 'i');
var objRE_r = new RegExp('(radio_)' + GUID + '_(r)', 'i');
var objRE_a = new RegExp('(object_)' + GUID + '_(a)', 'i');
var objRE_m = new RegExp('(radio_)' + GUID + '_(m)', 'i');
var objRE_d = new RegExp('(radio_)' + GUID + '_(d)', 'i');
// helper data structures used by optimized algorithm
var aryObjList = new Array();
var aryRList = new Array();
var aryAList = new Array();
var aryMList = new Array();
var aryDList = new Array();
var aryList = {
r: aryRList,
a: aryAList,
m: aryMList,
d: aryDList
}
// one pass scan to traverse the nodes collection (coll) to build functorListEx
// and intermediate arrays
for (var i=0,imax=coll.length; i<imax; i++) {
var _obj = coll[i];
if (!_obj.checked) continue; // <-- 更快的检测
if (_obj.type != "checkbox" && _obj.type != "radio") continue;
var id = _obj.id, name = _obj.name;
var _match = id.match(fnRE) || name.match(objRE_r) || id.match(objRE_a) ||
name.match(objRE_m) || name.match(objRE_d) || id.match(objRE);
if (!_match) continue;
var tag = _match[3], tag2 = tag+'_', guid = _match[2];
switch (tag) {
'a': aryList[tag][guid] = tag2; break;
'r', 'm', 'd':
aryList[tag][guid] = tag2 + _obj.value; break;
default :
if (_match[1]=='functor_') {
functorListEx.append(guid, ";")
}
else { // for _match[1]=='object_'
aryObjList.push(guid)
}
}
}
// further process to build objectListEx from the intermediate arrays
for (var i=0, imax=aryObjList.length; i<imax; i++) {
var id = aryObjList[i];
var r = aryRList[id] || "";
var a = aryAList[id] || "";
var m = aryMList[id] || "";
var d = aryDList[id] || "";
objectListEx.append(id , ":" , r , ":" , a , ":" , m , ":" , d , ";");
}
七、又一处小的优化
---
刚才想了想,其实上面代码中的switch还是啰嗦了。下面做一下下小的优化:
switch (_match[1] + tag) {
'functor_undefined': functorListEx.append(guid, ";"); break;
'object_undefined': aryObjList.push(guid); break;
'object_a': aryList[tag][guid] = tag2 ; break;
default: // for r,m,d
aryList[tag][guid] = tag2 + _obj.value;
}