知道了效率低下的原因,要从根本上进行解决,自然要想方设法来合并循环,初略一看,按照上边代码中注释里的数字,至少以下几点是可以合并的:
3和4可以合并为一次循环,从120+60+120+60变为120,减少了240。1、2和5可以合并为一次循环,从6+720+726变为726,减少了726。6、7、8可以合并为一次循环,从120+120+120变为120,减少了240。进一步的,3、4和6、7、8一样可以合并为一次循环,继续减少了120。累加一下,我们一共减少了240+726+240+120=1326次元素操作,总计13260ms。在优化之后,我们的函数耗时变为21720-13260=8460ms,即8s。
到这里可能会有一个疑问,从表格的结构上来说,所有的th和td元素肯定都在tr之内,那么为什么不将1、2、5这三步的循环同样放到对tr的循环中,形成一个嵌套的循环,这样不是更加快速吗?
这里之所以没有这么做,主要有2个原因:
其一,无论将1、2、5这三者放在哪里,都不会减少对所有th和td元素的一次访问。
另一方面,$(‘th,td’)这个选择器,在sizzle中会被翻译成2次getElementsByTagName函数的调用,第一次获取所有th,第二次获取所有td,然后进行集合的归并。由于getElementsByTagName是内置函数,在此可以认为该函数是不带循环的,即复杂度为O(1),同样集合的归并使用Array的相关函数,是对内存的操作,复杂度同样为O(1)。
反之,如果在对tr元素的循环中再采用$(‘th,’td)这个选择器,则是在tr元素上调用2次getElementsByTagName,由于无论在哪个元素上调用该函数,函数执行的时间是相同的,因此在循环tr时使用,反而多出了119*2次的函数调用,效率不升反降。
可见,对sizzle选择器的基本知识,也是帮助优化jQuery代码的很重要的一方面。
不要啥都让javascript来做。
根据前面的基本的优化,已经将时间从21秒降到了8秒,但是8秒这个数字显然是无法接受的。
再进一步分析我们的代码,事实上,循环遍历是语言层面上的内容,其速度应该是相当快的。而针对每个元素所做的操作,是jQuery提供的函数,相比遍历来说,才是占去大部分资源的主子。如果说遍历中访问元素用时是10ms的话,不客气地说执行一个addClass至少是100ms级别的消耗。
因此,为了进一步地优化效率,就不得不从减少对元素的操作入手。再仔细地回审代码,发现这个函数有着非常多的对样式的修改,其中至少包括了:
给所有th加上class。
给所有td加上class。
给tr分奇偶行加上class。
给所有th和td加上一个text-align样式。
而事实上我们知道,CSS本身就拥有子代选择器,而浏览器原生对CSS的解析,效率远远高于让javascript去给元素一一加上class。
所以,如果对CSS是可控的,那么这个函数就不应该拥有headerClass、cellClass这两个配置项,而是尽可能地在CSS中进行配置:










