搜索
首页web前端js教程JQuery each()函数如何优化循环DOM结构的性能_jquery

如果对jQuery这东西只停留在用的层面,而不知其具体实现的话,真的很容易用出问题来。这也是为什么近期我一直不怎么推崇用jQuery,这框架的API设定就有误导人们走上歧途之嫌。

复制代码 代码如下:

$.fn.beautifyTable = function(options) {
//定义默认配置项,再用options覆盖
return this.each(function() {
var table = $(this),
tbody = table.children('tbody'),
tr = tbody.children('tr'),
th = tbody.children('th'),
td = tbody.children('td');
//单独内容的class
table.addClass(option.tableClass);
th.addClass(options.headerClass); //1
td.addClass(options.cellClass); //2
//奇偶行的class
tbody.children('tr:even').addClass(options.evenRowClass); //3
tbody.children('tr:odd').addClass(options.oddRowClass); //4
//对齐方式
tr.children('th,td').css('text-align', options.align); //5
//添加鼠标悬浮
tr.bind('mouseover', addActiveClass); //6
tr.bind('mouseout', removeActiveClass); //7
//点击变色
tr.bind('click', toggleClickClass); //8
});
};

总的来说,这段代码不错,思路清晰,逻辑明确,想要做什么也通过注释说得很明白了。但是按作者的说法,当表格中有120行时,IE已经反映脚本运行时间过长了。显然从表现来看,这个函数的效率不高,甚至说极其低下。

于是,开始从代码层面进行分析,这是一个标准的jQuery插件式的函数,有个典型的return this.each(function( ) { 。.. };);形式的代码,如果作者写下这段代码的时候,不是照本宣科不经思考的话,就应该意识到jQuery的一个函数干了什么事。

简单来说,jQuery.fn下的函数,绝大部分是一个each的调用,所谓each,自然是对选择出来的元素进行了遍历,并对某个元素进行了指定的操作。那么看看上面一段代码,进行了多少的遍历,在此就假设只选择了120行,每一行有6列,另加上1行的表头吧:
遍历th,添加headerClass,元素数为6。
遍历td,添加cellClass,元素数为6*120=720。
从所有tr中找出奇数的,需要对所有tr进行一次遍历,元素数为120。
遍历奇数的tr,添加evenRowClass,元素数为120/2=60。
从所有tr中找出偶数的,需要对所有tr进行一次遍历,元素数为120。
遍历偶数的tr,添加oddRowClass,元素数为120/2=60。
遍历所有th和td,添加text-align,元素数为120*6+6=726。
遍历所有tr,添加mouseover事件,元素数为120。
遍历所有tr,添加mouseout事件,元素数为120。
遍历所有tr,添加click事件,元素数为120。
为了方便,我们简单地假设,在遍历中访问一个元素耗时为10ms,那么这个函数一共用了多少时间呢?这个函数共遇上了2172个元素,耗时21720ms,即21秒,显然IE确实应该报脚本执行过久了。

知道了效率低下的原因,要从根本上进行解决,自然要想方设法来合并循环,初略一看,按照上边代码中注释里的数字,至少以下几点是可以合并的:
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中进行配置:
复制代码 代码如下:

.beautiful-table th { /* headerClass的内容 */ }
.beautiful-table td { /* cellClass的内容 */ }

再者,对于tr的奇偶行样式,在部分浏览器下可以使用:nth-child伪类来实现,这方面可以利用特性探测,仅在不支持该伪类的浏览器中使用addClass添加样式。当然如果你仅仅想对IE系列进行优化的话,这一条可以忽略了。

对于:nth-child伪类的探测,可以用以下的思路来进行:创建一个stylesheet,再创建一条规则,如#test span:nth-child(odd) { display: block; }。创建相应的HTML结构,一个id为test的div,内部放置3个span。

将stylesheet和div一同加入的DOM树中。查看第1和第3个span的运行期display样式,如果是block,则表明支持该伪类。删除创建的stylesheet和div,别忘了缓存探测的结果。最后,对于给所有th和td元素添加text-align样式,也是可以通过css进行优化的。既然不知道添加的是哪个align,那么就多写几个样式:
复制代码 代码如下:

/* CSS样式 */
.beautiful-table-center th,.beautiful-table-center td { text-align: center !important; }
.beautiful-table-rightright th,.beautiful-table-rightright td { text-align: rightright !important; }
.beautiful-table-left th,.beautiful-table-left td { text-align: left !important; }
/* javascript */
table.addClass('beautiful-table-' + options.align);

当然,上面所说的优化,是建立在对CSS有控制权的情况下的,如果本身无法接触到CSS样式,比如这是一个通用的插件函数,会被完全无法控制的第三方使用,那么怎么办呢?也不是完全没有办法:
去找页面里的所有CSS规则,比如document.styleSheets。遍历所有规则,把配置项中的headerClass、cellClass等拿出来。提取需要的几个class中的所有样式,再自己组装成新的选择器,如beautiful-table th。使用创建出来的选择器,生成新的stylesheet,加入到DOM树中。那么只给table加上beautiful-table这个class就搞定了。

当然上面的做法其实也蛮消耗时间的,毕竟又要遍历stylesheet,又要创建stylesheet。具体是不是对效率提升有很大的帮助,则依据页面的规模会有不同的效果,是否使用就要看函数设计人员的具体需求了,这里也就是提一种策略。

总的来说,通过尽可能少地执行javascript,将更多的样式化的任务交给CSS,则浏览器的渲染引擎来完成,又可以进一步地优化该函数,假设对addClass、css的调用需要100ms的话,此次优化直接消灭了原有120+726=846次的操作,节约了84600ms的时间(当然有夸张的成分,但是对整个函数的消耗来说,这个确实是很大的一块)。

这篇文章,仅仅是想在jQuery的各个实现的层面上来进行优化,只涉及到了对jQuery整个运行过程的分析、细节介绍和优化方向,并没有提到一些基本之基本的优化方法,比如:先将整个table从DOM树中移除,完成所有的操作之后再放回DOM,减少repaint。将mouseover和mouseout改为mouseenter和mouseleave,减少因为下正确的事件冒泡模型导致的重复的事件函数的执行。对于th、td之类单纯元素的选择,优先考虑使用原生的getElementsByTagName,消灭sizzle分析选择器的时间。

最后,这篇文章只是想说明,对于前端开发人员,虽然浏览器可能是个黑盒,但是很多框架、工具、库都是开放的,在使用之前如果可以进行一定程度的了解,必然有助于个人的技术提升和最终产品的质量优化,“知其然而不知其所以然”是非常忌讳的情况。
声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
JavaScript,C和浏览器之间的关系JavaScript,C和浏览器之间的关系May 01, 2025 am 12:06 AM

引言我知道你可能会觉得奇怪,JavaScript、C 和浏览器之间到底有什么关系?它们之间看似毫无关联,但实际上,它们在现代网络开发中扮演着非常重要的角色。今天我们就来深入探讨一下这三者之间的紧密联系。通过这篇文章,你将了解到JavaScript如何在浏览器中运行,C 在浏览器引擎中的作用,以及它们如何共同推动网页的渲染和交互。JavaScript与浏览器的关系我们都知道,JavaScript是前端开发的核心语言,它直接在浏览器中运行,让网页变得生动有趣。你是否曾经想过,为什么JavaScr

node.js流带打字稿node.js流带打字稿Apr 30, 2025 am 08:22 AM

Node.js擅长于高效I/O,这在很大程度上要归功于流。 流媒体汇总处理数据,避免内存过载 - 大型文件,网络任务和实时应用程序的理想。将流与打字稿的类型安全结合起来创建POWE

Python vs. JavaScript:性能和效率注意事项Python vs. JavaScript:性能和效率注意事项Apr 30, 2025 am 12:08 AM

Python和JavaScript在性能和效率方面的差异主要体现在:1)Python作为解释型语言,运行速度较慢,但开发效率高,适合快速原型开发;2)JavaScript在浏览器中受限于单线程,但在Node.js中可利用多线程和异步I/O提升性能,两者在实际项目中各有优势。

JavaScript的起源:探索其实施语言JavaScript的起源:探索其实施语言Apr 29, 2025 am 12:51 AM

JavaScript起源于1995年,由布兰登·艾克创造,实现语言为C语言。1.C语言为JavaScript提供了高性能和系统级编程能力。2.JavaScript的内存管理和性能优化依赖于C语言。3.C语言的跨平台特性帮助JavaScript在不同操作系统上高效运行。

幕后:什么语言能力JavaScript?幕后:什么语言能力JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript在浏览器和Node.js环境中运行,依赖JavaScript引擎解析和执行代码。1)解析阶段生成抽象语法树(AST);2)编译阶段将AST转换为字节码或机器码;3)执行阶段执行编译后的代码。

Python和JavaScript的未来:趋势和预测Python和JavaScript的未来:趋势和预测Apr 27, 2025 am 12:21 AM

Python和JavaScript的未来趋势包括:1.Python将巩固在科学计算和AI领域的地位,2.JavaScript将推动Web技术发展,3.跨平台开发将成为热门,4.性能优化将是重点。两者都将继续在各自领域扩展应用场景,并在性能上有更多突破。

Python vs. JavaScript:开发环境和工具Python vs. JavaScript:开发环境和工具Apr 26, 2025 am 12:09 AM

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

JavaScript是用C编写的吗?检查证据JavaScript是用C编写的吗?检查证据Apr 25, 2025 am 12:15 AM

是的,JavaScript的引擎核心是用C语言编写的。1)C语言提供了高效性能和底层控制,适合JavaScript引擎的开发。2)以V8引擎为例,其核心用C 编写,结合了C的效率和面向对象特性。3)JavaScript引擎的工作原理包括解析、编译和执行,C语言在这些过程中发挥关键作用。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)