- 浏览: 368033 次
- 性别:
最新评论
-
天使建站:
这里这篇文章更详细 还有完整的实例演示:js跳出循环:brea ...
js跳出循环的方法区别(break,continue,return) -
jahentao:
我觉得Jcreator和eclipse中的列出属性和方法,很多 ...
反射原理 -
T240178168:
相互交流下吧
ie9以下都有这个问题(ajax) -
blackproof:
试了一下,的确第一种写法oracle优化了,效率比第二种快多了 ...
Oracle分页sql语句 -
杨白白:
进程与线程
1.使用局部变量避免使用全局变量
比如
function test(){
var s = document.getElementById('aaa');
s.innerHTML = document.body.clientHeight;
}
改成
function test(){
var d = document,
s = d.getElementById('aaa');
s.innerHTML = d.body.clientHeight;
}
局部变量的好处就是减少了作用域链的查找
我建议要是有两次的引用就用局部变量
2.避免使用with(这个估计地球人都知道)
我理解原因就是with会创建自己的作用域,这样就加长了原来的作用域链,使得在with块中执行的代码反而变慢了,在书写上好像省了代码,其实在访问上反而变长变繁琐了,性能下降了
例子
使用with
function test(){
with(document.body){
clientHeight = '200px';
clientWidth = '200px'
}
}
其实都可以写成
function test(){
var ds = document.body;
ds.clientHeight = '200px';
ds.clientWidth = '200px'
}
3. 遍历nodelist的方式
一般的方式都是
var as = document.getElementsByTagName('div');
for(var i=0,l<as.length;i<l;i++){//只计算一次长度}
我的方式一次都不用
for(var i=0,ci;ci=as[i++];){}当nodeList完结时就为false就结束了
好处,没计算长度,省了在循环里赋值,代码更加少了,i++到了判断里
(注意:这个方式用在nodelist里可以,如果你用到array里,可会有问题的,数组里有个0后者null什么的就瞎了)
4.别用那么多个var,一个加逗号就搞定了
var a =1;
var b = 1;
var c =1;
代码长,性能差
拆成
var a=1,
b=1,
c=1;
5.innerHTML是最好的选择
往元素添加元素时,最好用innerHTML
6.ie的removeChild不好用
一般咱们删除一个元素会用
elm.removeChild(subElm)
这个在ie下不好用,因为在ie下这个只是把这个元素从dom树中断开了,但并没用真正删除,它现在变成了孤立的节点了,
要想真正删除,可以这样
var ryc = document.createElement('div');
div.appendChild(subElm);
div.innerHTML = '';
div = null;
这样就真的删除了,除了ie外别的都可以用removeChild达到效果
7.为多个同级元素绑定事件时,不用为每个都绑定,为他们的父级绑定就行了
比如
<ul id="a">
sdf sdf sdf sdf sdf sdf
</ul>
可能你要为每个li添加click
为每个添加那可繁琐且容易出现溢出(ie)
其实只要为 ul一个添加就行了,因为事件是冒泡向上的
var ul = document.getElementById('a');
ul.onclick = function (e){
!e&&(e=event);
var target = e.srcElement||e.target;
if(target.tagName=='LI'){
//your code
}
}
8.尽量用原生的方法,因为原生的都是用c/c++编译而成的他们执行的要比用js写的方法快多了
9.appendChild用的多时一定要用docuemntfragment
比如
for(var i=0;i<1000;i++){
var o = document.createElement('div');
document.body.appendChild(o);
}
用documentFragment
var f = document.createDocumentFragment();
for(var i=0;i<1000;i++){
var o = document.createElement('div');
f.appendChild(o);
}
document.body.appendChild(f);
10. if else用的>=3个了,那用switch吧,好阅读,性能好
11. if<=3,别用if了,用3元表达式吧
12. if==1,if改&&
if(a==1)a=2
改
a==1&&(a=2);
13.计算元素位置,while()offsetParent
这个方式是老方式了,现在的浏览器ie6以上,ff3.1以上,chrome,opera(我只测了最新的)都支持这个
el.getBoundingClientRect返回一个对像,分别是top,left,right,bottom的值
14.正则的查找没有indexOf快
var s= 'sdfsdfsdfAAAsdfdsfs';
for(var i=0;i<1000;i++){
s.indexOf('AAA')
}
比这个快
var s= 'sdfsdfsdfAAAsdfdsfs';
for(var i=0;i<1000;i++){
/AAA/.test(s)
}
15.在正则中多用非捕获(?:)这样快
16.设置某个元素的style时用cssText简单些
el.style.cssText +=";postion:absolute;"
(注意:position前;不能去了,因为ie没有这个;position认不出来了就,比的浏览器没这个毛病)
17.在new 时,没有参数时函数名后边的括号可以去了
new fn()==>new fn
new Image()==>new Image
首先,与其他语言不同,JS的效率很大程度是取决于JS engine的效率。除了引擎实现的优劣外,引擎自己也会为一些特殊的代码模式采取一些优化的策略。例如FF、Opera和Safari的JS引擎,都对 字符串的拼接运算(+)做了特别优化。显然,要获得最大效率,就必须要了解引擎的脾气,尽量迎合引擎的口味。所以对于不同的引擎,所作的优化极有可能是背 道而驰的。
而如果做跨浏览器的web编程,则最大的问题是在于IE6(JScript 5.6)!因为在不打hotfix的情况下,JScript引擎的垃圾回收的bug,会导致其在真实应用中的performance跟其他浏览器根本不在一个数量级上。因此在这种场合做优化,实际上就是为JScript做优化!
所以第一原则就是只需要为IE6(未打补丁的JScript 5.6或更早版本)做优化!
如果你的程序已经优化到在IE6下可以接受的性能,那基本上在其他浏览器上性能就完全没有问题。
因此,注意我下面讲的许多问题在其他引擎上可能完全不同,例如在循环中进行字符串拼接,通常认为需要用Array.join的方式,但是由于 SpiderMonkey等引擎对字符串的“+”运算做了优化,结果使用Array.join的效率反而不如直接用“+”!但是如果考虑IE6,则其他浏 览器上的这种效率的差别根本不值一提。
JS优化与其他语言的优化也仍然有相同之处。比如说,不要一上来就急吼吼的做优化,那样毫无意义。优化的关键,仍然是要把精力放在最关键的地方, 也就是瓶颈上。一般来说,瓶颈总是出现在大规模循环的地方。这倒不是说循环本身有性能问题,而是循环会迅速放大可能存在的性能问题。
所以第二原则就是以大规模循环体为最主要优化对象。
以下的优化原则,只在大规模循环中才有意义,在循环体之外做此类优化基本上是没有意义的。
目前绝大多数JS引擎都是解释执行的,而解释执行的情况下,在所有操作中,函数调用的效率是较低的。此外,过深的prototype继承链或者多 级引用也会降低效率。JScript中,10级引用的开销大体是一次空函数调用开销的1/2。这两者的开销都远远大于简单操作(如四则运算)。
所以第三原则就是尽量避免过多的引用层级和不必要的多次方法调用。
特别要注意的是,有些情况下看似是属性访问,实际上是方法调用。例如所有DOM的属性,实际上都是方法。在遍历一个NodeList的时候,循环 条件对于nodes.length的访问,看似属性读取,实际上是等价于函数调用的。而且IE DOM的实现上,childNodes.length每次是要通过内部遍历重新计数的。(My god,但是这是真的!因为我测过,childNodes.length的访问时间与childNodes.length的值成正比!)这非常耗费。所以 预先把nodes.length保存到js变量,当然可以提高遍历的性能。
同样是函数调用,用户自定义函数的效率又远远低于语言内建函数,因为后者是对引擎本地方法的包装,而引擎通常是c,c++,java写的。进一步,同样的功能,语言内建构造的开销通常又比内建函数调用要效率高,因为前者在JS代码的parse阶段就可以确定和优化。
所以第四原则就是尽量使用语言本身的构造和内建函数。
这里有一个例子是高性能的String.format方法。 String.format传统的实现方式是用String.replace(regex, func),在pattern包含n个占位符(包括重复的)时,自定义函数func就被调用n次。而这个高性能实现中,每次format调用所作的只是一 次Array.join然后一次String.replace(regex, string)的操作,两者都是引擎内建方法,而不会有任何自定义函数调用。两次内建方法调用和n次的自定义方法调用,这就是性能上的差别。
同样是内建特性,性能上也还是有差别的。例如在JScript中对于arguments的访问性能就很差,几乎赶上一次函数调用了。因此如果一个 可变参数的简单函数成为性能瓶颈的时候,可以将其内部做一些改变,不要访问arguments,而是通过对参数的显式判断来处理。
比如:
Java代码
1. function sum() {
2. var r = 0;
3. for (var i = 0; i < arguments.length; i++) {
4. r += arguments[i];
5. }
6. return r;
7. }
这个sum通常调用的时候个数是较少的,我们希望改进它在参数较少时的性能。如果改成:
Java代码
1. function sum() {
2. switch (arguments.length) {
3. case 1: return arguments[0];
4. case 2: return arguments[0] + arguments[1];
5. case 3: return arguments[0] + arguments[1] + arguments[2];
6. case 4: return arguments[0] + arguments[1] + arguments[2] + arguments[3];
7. default:
8. var r = 0;
9. for (var i = 0; i < arguments.length; i++) {
10. r += arguments[i];
11. }
12. return r;
13. }
14. }
其实并不会有多少提高,但是如果改成:
Java代码
1. function sum(a, b, c, d, e, f, g) {
2. var r = a ? b ? c ? d ? e ? f ? a + b + c + d + e + f : a + b + c + d + e : a + b + c + d : a + b + c : a + b : a : 0;
3. if (g === undefined) return r;
4. for (var i = 6; i < arguments.length; i++) {
5. r += arguments[i];
6. }
7. return r;
8. }
就会提高很多(至少快1倍)。
最后是第五原则,也往往是真实应用中最重要的性能障碍,那就是尽量减少不必要的对象创建。
本身创建对象是有一定的代价的,但是这个代价其实并不大。最根本的问题是由于JScript愚蠢之极的垃圾回收调度算法,导致随着对象个数的增加,性能严重下降(据微软的人自己说复杂度是O(n^2))。
比如我们常见的字符串拼接问题,经过我的测试验证,单纯的多次创建字符串对象其实根本不是性能差的原因。要命的是在对象创建期间的无谓的垃圾回收的开销。而Array.join的方式,不会创建中间字符串对象,因此就减少了那该死的垃圾回收的开销。
因此,如果我们能把大规模对象创建转化为单一语句,则其性能会得到极大的提高!例如通过构造代码然后eval——实际上PIES项目中正在根据这个想法来做一个专门的大规模对象产生器……
好了上面就是偶总结的JS优化五大原则。
一、正则表达式的创建代码
---
这样的创建代码实在冗余:
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 (_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;
比如
function test(){
var s = document.getElementById('aaa');
s.innerHTML = document.body.clientHeight;
}
改成
function test(){
var d = document,
s = d.getElementById('aaa');
s.innerHTML = d.body.clientHeight;
}
局部变量的好处就是减少了作用域链的查找
我建议要是有两次的引用就用局部变量
2.避免使用with(这个估计地球人都知道)
我理解原因就是with会创建自己的作用域,这样就加长了原来的作用域链,使得在with块中执行的代码反而变慢了,在书写上好像省了代码,其实在访问上反而变长变繁琐了,性能下降了
例子
使用with
function test(){
with(document.body){
clientHeight = '200px';
clientWidth = '200px'
}
}
其实都可以写成
function test(){
var ds = document.body;
ds.clientHeight = '200px';
ds.clientWidth = '200px'
}
3. 遍历nodelist的方式
一般的方式都是
var as = document.getElementsByTagName('div');
for(var i=0,l<as.length;i<l;i++){//只计算一次长度}
我的方式一次都不用
for(var i=0,ci;ci=as[i++];){}当nodeList完结时就为false就结束了
好处,没计算长度,省了在循环里赋值,代码更加少了,i++到了判断里
(注意:这个方式用在nodelist里可以,如果你用到array里,可会有问题的,数组里有个0后者null什么的就瞎了)
4.别用那么多个var,一个加逗号就搞定了
var a =1;
var b = 1;
var c =1;
代码长,性能差
拆成
var a=1,
b=1,
c=1;
5.innerHTML是最好的选择
往元素添加元素时,最好用innerHTML
6.ie的removeChild不好用
一般咱们删除一个元素会用
elm.removeChild(subElm)
这个在ie下不好用,因为在ie下这个只是把这个元素从dom树中断开了,但并没用真正删除,它现在变成了孤立的节点了,
要想真正删除,可以这样
var ryc = document.createElement('div');
div.appendChild(subElm);
div.innerHTML = '';
div = null;
这样就真的删除了,除了ie外别的都可以用removeChild达到效果
7.为多个同级元素绑定事件时,不用为每个都绑定,为他们的父级绑定就行了
比如
<ul id="a">
</ul>
可能你要为每个li添加click
为每个添加那可繁琐且容易出现溢出(ie)
其实只要为 ul一个添加就行了,因为事件是冒泡向上的
var ul = document.getElementById('a');
ul.onclick = function (e){
!e&&(e=event);
var target = e.srcElement||e.target;
if(target.tagName=='LI'){
//your code
}
}
8.尽量用原生的方法,因为原生的都是用c/c++编译而成的他们执行的要比用js写的方法快多了
9.appendChild用的多时一定要用docuemntfragment
比如
for(var i=0;i<1000;i++){
var o = document.createElement('div');
document.body.appendChild(o);
}
用documentFragment
var f = document.createDocumentFragment();
for(var i=0;i<1000;i++){
var o = document.createElement('div');
f.appendChild(o);
}
document.body.appendChild(f);
10. if else用的>=3个了,那用switch吧,好阅读,性能好
11. if<=3,别用if了,用3元表达式吧
12. if==1,if改&&
if(a==1)a=2
改
a==1&&(a=2);
13.计算元素位置,while()offsetParent
这个方式是老方式了,现在的浏览器ie6以上,ff3.1以上,chrome,opera(我只测了最新的)都支持这个
el.getBoundingClientRect返回一个对像,分别是top,left,right,bottom的值
14.正则的查找没有indexOf快
var s= 'sdfsdfsdfAAAsdfdsfs';
for(var i=0;i<1000;i++){
s.indexOf('AAA')
}
比这个快
var s= 'sdfsdfsdfAAAsdfdsfs';
for(var i=0;i<1000;i++){
/AAA/.test(s)
}
15.在正则中多用非捕获(?:)这样快
16.设置某个元素的style时用cssText简单些
el.style.cssText +=";postion:absolute;"
(注意:position前;不能去了,因为ie没有这个;position认不出来了就,比的浏览器没这个毛病)
17.在new 时,没有参数时函数名后边的括号可以去了
new fn()==>new fn
new Image()==>new Image
首先,与其他语言不同,JS的效率很大程度是取决于JS engine的效率。除了引擎实现的优劣外,引擎自己也会为一些特殊的代码模式采取一些优化的策略。例如FF、Opera和Safari的JS引擎,都对 字符串的拼接运算(+)做了特别优化。显然,要获得最大效率,就必须要了解引擎的脾气,尽量迎合引擎的口味。所以对于不同的引擎,所作的优化极有可能是背 道而驰的。
而如果做跨浏览器的web编程,则最大的问题是在于IE6(JScript 5.6)!因为在不打hotfix的情况下,JScript引擎的垃圾回收的bug,会导致其在真实应用中的performance跟其他浏览器根本不在一个数量级上。因此在这种场合做优化,实际上就是为JScript做优化!
所以第一原则就是只需要为IE6(未打补丁的JScript 5.6或更早版本)做优化!
如果你的程序已经优化到在IE6下可以接受的性能,那基本上在其他浏览器上性能就完全没有问题。
因此,注意我下面讲的许多问题在其他引擎上可能完全不同,例如在循环中进行字符串拼接,通常认为需要用Array.join的方式,但是由于 SpiderMonkey等引擎对字符串的“+”运算做了优化,结果使用Array.join的效率反而不如直接用“+”!但是如果考虑IE6,则其他浏 览器上的这种效率的差别根本不值一提。
JS优化与其他语言的优化也仍然有相同之处。比如说,不要一上来就急吼吼的做优化,那样毫无意义。优化的关键,仍然是要把精力放在最关键的地方, 也就是瓶颈上。一般来说,瓶颈总是出现在大规模循环的地方。这倒不是说循环本身有性能问题,而是循环会迅速放大可能存在的性能问题。
所以第二原则就是以大规模循环体为最主要优化对象。
以下的优化原则,只在大规模循环中才有意义,在循环体之外做此类优化基本上是没有意义的。
目前绝大多数JS引擎都是解释执行的,而解释执行的情况下,在所有操作中,函数调用的效率是较低的。此外,过深的prototype继承链或者多 级引用也会降低效率。JScript中,10级引用的开销大体是一次空函数调用开销的1/2。这两者的开销都远远大于简单操作(如四则运算)。
所以第三原则就是尽量避免过多的引用层级和不必要的多次方法调用。
特别要注意的是,有些情况下看似是属性访问,实际上是方法调用。例如所有DOM的属性,实际上都是方法。在遍历一个NodeList的时候,循环 条件对于nodes.length的访问,看似属性读取,实际上是等价于函数调用的。而且IE DOM的实现上,childNodes.length每次是要通过内部遍历重新计数的。(My god,但是这是真的!因为我测过,childNodes.length的访问时间与childNodes.length的值成正比!)这非常耗费。所以 预先把nodes.length保存到js变量,当然可以提高遍历的性能。
同样是函数调用,用户自定义函数的效率又远远低于语言内建函数,因为后者是对引擎本地方法的包装,而引擎通常是c,c++,java写的。进一步,同样的功能,语言内建构造的开销通常又比内建函数调用要效率高,因为前者在JS代码的parse阶段就可以确定和优化。
所以第四原则就是尽量使用语言本身的构造和内建函数。
这里有一个例子是高性能的String.format方法。 String.format传统的实现方式是用String.replace(regex, func),在pattern包含n个占位符(包括重复的)时,自定义函数func就被调用n次。而这个高性能实现中,每次format调用所作的只是一 次Array.join然后一次String.replace(regex, string)的操作,两者都是引擎内建方法,而不会有任何自定义函数调用。两次内建方法调用和n次的自定义方法调用,这就是性能上的差别。
同样是内建特性,性能上也还是有差别的。例如在JScript中对于arguments的访问性能就很差,几乎赶上一次函数调用了。因此如果一个 可变参数的简单函数成为性能瓶颈的时候,可以将其内部做一些改变,不要访问arguments,而是通过对参数的显式判断来处理。
比如:
Java代码
1. function sum() {
2. var r = 0;
3. for (var i = 0; i < arguments.length; i++) {
4. r += arguments[i];
5. }
6. return r;
7. }
这个sum通常调用的时候个数是较少的,我们希望改进它在参数较少时的性能。如果改成:
Java代码
1. function sum() {
2. switch (arguments.length) {
3. case 1: return arguments[0];
4. case 2: return arguments[0] + arguments[1];
5. case 3: return arguments[0] + arguments[1] + arguments[2];
6. case 4: return arguments[0] + arguments[1] + arguments[2] + arguments[3];
7. default:
8. var r = 0;
9. for (var i = 0; i < arguments.length; i++) {
10. r += arguments[i];
11. }
12. return r;
13. }
14. }
其实并不会有多少提高,但是如果改成:
Java代码
1. function sum(a, b, c, d, e, f, g) {
2. var r = a ? b ? c ? d ? e ? f ? a + b + c + d + e + f : a + b + c + d + e : a + b + c + d : a + b + c : a + b : a : 0;
3. if (g === undefined) return r;
4. for (var i = 6; i < arguments.length; i++) {
5. r += arguments[i];
6. }
7. return r;
8. }
就会提高很多(至少快1倍)。
最后是第五原则,也往往是真实应用中最重要的性能障碍,那就是尽量减少不必要的对象创建。
本身创建对象是有一定的代价的,但是这个代价其实并不大。最根本的问题是由于JScript愚蠢之极的垃圾回收调度算法,导致随着对象个数的增加,性能严重下降(据微软的人自己说复杂度是O(n^2))。
比如我们常见的字符串拼接问题,经过我的测试验证,单纯的多次创建字符串对象其实根本不是性能差的原因。要命的是在对象创建期间的无谓的垃圾回收的开销。而Array.join的方式,不会创建中间字符串对象,因此就减少了那该死的垃圾回收的开销。
因此,如果我们能把大规模对象创建转化为单一语句,则其性能会得到极大的提高!例如通过构造代码然后eval——实际上PIES项目中正在根据这个想法来做一个专门的大规模对象产生器……
好了上面就是偶总结的JS优化五大原则。
一、正则表达式的创建代码
---
这样的创建代码实在冗余:
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 (_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;
发表评论
-
ie9以下都有这个问题(ajax)
2016-01-18 17:01 1415ajax在IE下使用GET方式请求,如果地址和参数一样 会首先 ... -
Jquery绑定事件(bind和live的区别)
2015-04-27 21:56 1225Jquery中绑定事件有三种方法:以click事件为例 ... -
js字符串转换成数字
2015-01-14 23:33 4179js字符串转换成数字 js ... -
table中div内容为纯数字和字母换行
2014-12-29 20:01 1307不换行 简单些td中加white-space: nowrap; ... -
java 方式的将 java 对象以及 list 或者 map 转化为 json 数据
2014-03-12 23:40 1247JavajsonAjax.net 学会了在 j2ee中使用 ... -
query each方法跳出循环,并获得返回值
2014-03-12 23:26 1171return false:将停止循环 (就像在普通的循环中使用 ... -
js跳出循环的方法区别(break,continue,return)
2014-03-12 23:24 20718跟许多多态语言一样,js也有break,continue,re ... -
jsp中的乱码怎么解决
2014-03-06 21:41 8941、在jsp页中加入一条语句: <%@ page co ... -
image 不提交表单
2014-03-06 21:24 795默认input的type为image或者submit都会提交的 ... -
HTML表格中的nowrap是什么意思啊?
2013-11-03 22:34 2435HTML <td> 标签的 nowrap 属性 ... -
IFrame AND window对象
2013-07-20 01:07 1895var detialIframe=document.all(& ... -
window.onload
2013-07-20 01:02 1622window.onload 同时执行多个函数的解决方法 1. ... -
关键词高亮显示
2012-10-27 23:18 1152<!DOCTYPE html PUBLIC " ... -
前台javascript速度优化
2012-10-20 18:26 2706两个基本点 1. 择重避轻,有所取舍。 l ... -
简单javascript概述
2012-10-20 18:19 2494什么是JavaScript JavaScript ... -
禁止网页复制的代码
2012-09-11 22:59 2300<html> <head> < ... -
JS打印
2012-09-11 22:56 2491一、普通打印(整页打) 这个不用多说,直接用 引用:wi ... -
Javascript总结(代码篇)
2012-08-17 20:23 24871)Alert box <html> <h ... -
JS优化经验
2012-08-16 22:00 23031.定义局部变量。对于 ... -
Javascript继承
2012-08-15 22:40 2316(一)对象冒充 JScript code funct ...
相关推荐
本文将详细讨论针对IE6.0起作用的js优化方法,包括字符串拼接、for循环优化、减少页面重绘、作用域链查找优化和避免双重解释等方面。 首先,关于字符串拼接的优化,原始的字符串拼接通常使用+=操作符,这种做法在...
由于JavaScript是动态类型语言,其内存管理与编译型语言有所不同,因此理解JS的内存管理和优化对于提升应用程序性能至关重要。 **一、JS内存管理** JS的内存管理主要依赖于垃圾收集机制(Garbage Collection, GC)...
在这个名为"javascript 数据收集, js 数据收集sdk.zip"的压缩包中,我们很可能是得到了一个名为"dc-master"的数据采集SDK的源码库。这个SDK可能包含了用于收集、处理和展示数据的各种工具和模块。 首先,数据采集是...
为了提升开发效率和代码质量,本文提出了通过JavaScript(JS)文件来优化网站表单验证和提交代码的方案。以下是根据给定文件内容生成的详细知识点。 知识点一:JavaScript的表单验证和提交优化 JavaScript是一种...
JavaScript是一种广泛应用于网页和网络应用的脚本语言,它的强大在于能够动态地更新和操作网页内容,赋予网页交互性。这个“JavaScript经典代码收集”压缩包显然包含了各种实用且经典的JavaScript代码片段,对于学习...
综上所述,这个"深入js编程网页收集 超级经典 JavaScript"资源集合将涵盖JavaScript的各个方面,无论你是初学者还是有经验的开发者,都能从中受益,不断提升自己的技能。通过深入学习和实践,你将能够驾驭这门强大的...
【标题】"上百个超酷JS广告代码收集汇总 (第一辑)"所涵盖的知识点主要集中在JavaScript、CSS、HTML以及XML这四个技术领域。这个压缩包文件可能是为了展示和分享一系列利用JavaScript实现的创新和吸引人的广告效果...
JavaScript,简称JS,是一种广泛应用于Web开发的轻量级、解释型编程语言,以其灵活性和交互性在网页设计中占据...希望这个超级经典的JavaScript网页收集可以帮助你进一步提升技能,成为一名出色的JavaScript开发者。
“网络收集”这个文件名可能意味着这是一些从网络上搜集整理的资料,涵盖了不同来源的JavaScript知识,可能包括了开源项目、教程、问答网站上的解决方案等。 综合以上信息,这个压缩包可能包含以下JavaScript知识点...
3. 框架优化:选择性能优秀的框架,如Express.js或Django,并针对框架特性进行定制优化。 三、数据库优化 1. 索引优化:为常用查询字段建立索引,加快查询速度。 2. SQL优化:避免全表扫描,合理使用JOIN操作,...
--形成关键词分析报告及布署方案 搜索引擎友好化---撰写站点修改措施建议书 内容强化---资讯频道建立、对编辑团队的培训 内部优化---URL优化、各级页面DIV重构、关键字布局、目录逻辑结构调整、JS优化、专题页建设等...
SpeedRacer 是一款强大的工具,专为 JavaScript 开发者设计,用于收集和分析应用程序的性能指标。这个工具在 Chrome 浏览器环境中运行,通过执行特定的脚本来揭示 JavaScript 的执行效率、页面渲染速度以及帧率(fps...
CDN(Content Delivery Network)是一个关键的工具,它通过在全球部署多个节点,将静态资源如图片、CSS和JavaScript文件缓存到离用户最近的服务器上,从而减少网络延迟,提高页面加载速度。使用CDN可以有效缓解源...
在IT行业中,JavaScript(简称JS)是一种广泛使用的前端编程语言,尤其在网页开发中起着至关重要的作用。本文将深入探讨“一个对联广告的js”这个主题,它是一种利用JavaScript实现的对联广告功能,旨在帮助开发者...
Vue.js 是一个流行的前端JavaScript框架,由尤雨溪创建,旨在简化Web应用开发。它的核心理念是组件化,允许开发者将UI拆分为可复用的模块,从而提高代码的可维护性和效率。在"博客网_模板_javascript_vue.js_"这个...
标题“开发资料下载_Javascript资料搜集”提示我们这是一份关于JavaScript编程的资源集合,可能是为了帮助开发者学习和提升JavaScript技能。描述中的“NULL”没有提供额外信息,但提到的“博文链接:...
"javascript技巧精心收集"这个主题包含了一系列关于JavaScript编程的实用技巧和资源,旨在帮助开发者提升技能和效率。以下是一些从提供的文件名中提炼出的关键知识点: 1. **jsp技巧.txt**:这可能涉及到JavaServer...
首先,Blob.js是一个JavaScript库,它提供了在浏览器端创建Blob对象的能力。Blob对象在Web API中被用来表示不可变的原始数据,可以是任何类型的数据,如文本、图片或者二进制数据。在我们的场景中,Blob.js将帮助...