再议《JavaScript代码优化一例》

2024-01-17 11:38

本文主要是介绍再议《JavaScript代码优化一例》,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

2007年08月29日 18:07:00

今天收到《程序员》第8期,读到杨建华先生的"JavaSctipt代码优化一例"。仔细读来,觉得所做优化未能尽彻。所以专写一篇文章来讨论杨先生的例子。 原例可以在杨先生的blog上找到:
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;
}



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1764224


这篇关于再议《JavaScript代码优化一例》的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/615910

相关文章

Java Stream流与使用操作指南

《JavaStream流与使用操作指南》Stream不是数据结构,而是一种高级的数据处理工具,允许你以声明式的方式处理数据集合,类似于SQL语句操作数据库,本文给大家介绍JavaStream流与使用... 目录一、什么是stream流二、创建stream流1.单列集合创建stream流2.双列集合创建str

springboot集成easypoi导出word换行处理过程

《springboot集成easypoi导出word换行处理过程》SpringBoot集成Easypoi导出Word时,换行符n失效显示为空格,解决方法包括生成段落或替换模板中n为回车,同时需确... 目录项目场景问题描述解决方案第一种:生成段落的方式第二种:替换模板的情况,换行符替换成回车总结项目场景s

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

SpringBoot中@Value注入静态变量方式

《SpringBoot中@Value注入静态变量方式》SpringBoot中静态变量无法直接用@Value注入,需通过setter方法,@Value(${})从属性文件获取值,@Value(#{})用... 目录项目场景解决方案注解说明1、@Value("${}")使用示例2、@Value("#{}"php

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。