为了说明js性能方面的差异用一个简单的例子说明下,
<style>
#ul1{
padding: 5px;
overflow: hidden;
}
#ul1 li{
list-style: none;
width: 15px;
height: 15px;
border-radius: 50%;
background: #ccc;
margin: 3px;
float: left;
cursor: pointer;
}
#ul1 .active{
background: red;
}
</style>
<ul id="ul1">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
...
</ul>
这样一个很简单的列表。做成一个单选效果,给被点击的li添加一个active的class。
实现的发方法有很多种,我介绍4中,为了能清楚看到性能之间的差异,li元素我复制了2000个。
第一种方法,也是最不可取,性能也是最浪费的一种
console.time("time1");
$("#ul1 li").click(function(event) {
console.time("time2");
$("#ul1 li").removeClass('active');
$(this).addClass('active');
console.timeEnd("time2");
});
console.timeEnd("time1");
这方法可能是初学者最易用到的,他的性能如下
首次事件绑定用了11ms ,之后每次点击0.6ms左右,前几次用的时间比较多。
第二种方案
console.time("time1");
var arrli = $("#ul1 li");
arrli.click(function(event) {
console.time("time2");
arrli.removeClass('active');
$(this).addClass('active');
console.timeEnd("time2");
});
console.timeEnd("time1");
相对第一种情况首次花费时间多了,每次点击花费时间少了,这次把jquery dom做了缓存没有每次都进行dom选择
第三种方案,这种代码量比较多,采用了dom缓存,和虚拟dom的概念。避免没有必要的直接操作dom
同时为了提高性避开使用query的选择器
console.time("time1");
var arrli = [];
var lilist = document.querySelectorAll("#ul1 li");
for(var i=0,len=lilist.length;i<len;i++){
var jsn = {};
jsn._active = false;//代表该元素的选中状态[虚拟dom的概念]
jsn.dom = $(lilist[i]);//把源生对象转为query对象
arrli.push(jsn);
lilist[i].setAttribute("index",i);//添加一个数组中索引的属性
lilist[i].onclick = function(){
console.time("time2");
var index = this.getAttribute("index")*1;
for(var i=0,len=arrli.length;i<len;i++){
if(i === index){
//只有在没有选中的时候进行选中设置
if(!arrli[i]._active){
arrli[i]._active = true;
arrli[i].dom.addClass('active');
};
}else{
if(arrli[i]._active){
arrli[i]._active = false;
arrli[i].dom.removeClass('active');
};
};
};
console.timeEnd("time2");
};
};
console.timeEnd("time1");
看性能
首次耗时4.6ms,使用原生的选择器快了很多。每次点击最多的一次耗时也是0.1ms,所以相对前两种方案性能提高了很多。
第四种方案
基于第三种方案,每次点击的时候会循环下,这种方案避开这种循环
console.time("time1");
var arrli = [];
var lilist = document.querySelectorAll("#ul1 li");
var old = false;
for(var i=0,len=lilist.length;i<len;i++){
var jsn = {};
jsn._active = false;//代表该元素的选中状态[虚拟dom的概念]
jsn.dom = $(lilist[i]);//把源生对象转为query对象
arrli.push(jsn);
lilist[i].setAttribute("index",i);//添加一个数组中索引的属性
lilist[i].onclick = function(){
console.time("time2");
var index = this.getAttribute("index")*1;
//这个方案不需要循环
if(old){
old._active = false;
old.dom.removeClass('active');
};
if(!arrli[index]._active){
arrli[index]._active = true;
arrli[index].dom.addClass('active');
old = arrli[index];
};
console.timeEnd("time2");
};
};
console.timeEnd("time1");
性能
相对第三种代码改变并不多,性能方面每次点击所花费的时间有所提高,缺点是后两种增加了代码量