jQuery.unique()的实现方式
jQuery 中的 unique()
jQuery中 的 unique() 函数实现了对 DOM ELement按出现位置进行排序并去除重复元素的功能。使用方法如下:
<html>
<head></head>
<body onload="test()">
<div id="div_1">
<div id="div_2" />
</div>
<div id="div_3" />
<script type='text/javascript' src='jquery.js'></script>
<script type='text/javascript'>
function test() {
var divs = [
document.getElementById('div_3'),
document.getElementById('div_1'),
document.getElementById('div_2'),
document.getElementById('div_2'),
document.getElementById('div_1')
];
var html = show('before uinque', divs);
$.unique(divs);
html += show('after uinque', divs);
document.write(html);
}
function show(name, divs) {
var html = '';
for (var i = 0; i < divs.length; ++i) {
html += divs[i].getAttribute('id') + '<br />';
}
return name + ':<br />' + html + '<br />';
}
</script>
</body>
</html>
显示结果为:
before uinque:
div_3
div_1
div_2
div_2
div_1
after uinque:
div_1
div_2
div_3
从函数名来看,应该主要是用于去除重复元素的,为什么同时又先对 DOM Element 进行排序?为了解释这个问题,先让我们试一下不事先进行排序的情况下需要如何去除重复。
不排序的去除重复元素的实现
这里给出两种实现:
$ = function() {
return {
unique: function(elems) {
for (var i = 0; i < elems.length; ++i) {
for (var j = i + 1; j < elems.length; ++j) {
if (elems[i] === elems[j]) {
elems.splice(i--, 1);
break;
}
}
}
return elems;
},
unique2: function(elemsWithId) {
var obj = {};
for (var i = 0; i < elemsWithId.length; ++i) {
var elem = elemsWithId[i];
obj[elem.getAttribute('id')] = elem;
}
elemsWithId.length = 0;
for (var id in obj) {
elemsWithId.push(obj[id])
}
return elemsWithId;
}
}
}();
实现一使用了两重循环,算法复杂度为O(n^2);实现思路比较直观,即遍历数组,看每个元素是否与后面的元素重复,有重复则移除;但当DOM Element数量较多时性能较差,而jQuery中对大量元素进行去除重复的操作是很普遍的。
实现二将 Object 当做 HashMap/HashSet 来使用,算法复杂度为O(n);遗憾的是JavaScript中无法直接用 DOM ELement 作为 Object 的 key ,因此只能将 id 作为 key ,然而并非所有的 DOM Element 都是有 id 的,所以这种方法并不通用。而自己实现一个高性能的 HashSet(还需要自己动手计算 DOM Element 的 Hash Code ),工作量又比较大。
我们知道,基于比较的排序算法最多可以将算法复杂度降到O(nlgn),(比如结合使用快速排序和插入排序),之后遍历数组时只要比较相邻元素就可以了:
unique3: function(sortedElems) {
for ( var i = 1; i < sortedElems.length; i++ ) {
if ( sortedElems[i] === sortedElems[ i - 1 ] ) {
sortedElems.splice( i--, 1 );
}
}
return sortedElems;
}
JavaScript中又有内置的排序算法。因此,在JavaScript中,先排序后去除重复是较好的做法。
先排序后去除重复
先排序后去除重复的实现方式如下:
$ = function() {
var sort = function(a, b){
var aParents = getParents(a),
bParents = getParents(b),
al = aParents.length,
bl = bParents.length,
i, ap, bp;
for (i = 0; i < al && i < bl; i++) {
ap = aParents[i];
bp = bParents[i];
if (ap !== bp) {
return siblingCheck(ap, bp);
}
}
return i === al ?
siblingCheck(a, bp, -1) :
siblingCheck(ap, b, 1);
};
var getParents = function(elem) {
var ret = [], cur;
for (cur = elem; cur; cur = cur.parentNode) {
ret.unshift(cur);
}
return ret;
}
var siblingCheck = function(a, b, ret) {
if (a === b) { return ret; }
var cur = a;
while (cur && (cur = cur.nextSibling)) {
if (cur === b) {
return -1;
}
}
return 1;
};
return {
unique : function(elems) {
elems.sort(sort);
for ( var i = 1; i < elems.length; i++ ) {
if ( elems[i] === elems[ i - 1 ] ) {
elems.splice( i--, 1 );
}
}
return elems;
}
}
}();
这里使用了 Array 的 sort(...) 方法,并传入自定义的排序函数(sort函数)。
sort函数的做法是获取两个被比较元素的所有“直系祖宗”,从而确定了两个元素在 DOM 树中的位置;一般来说,两个元素有共同的根,那么就从根元素开始依次向下遍历,直到“分叉点”,再对分叉点的元素进行比较。如果直到遍历结束,仍未能达到分叉点(如元素a先遍历完,那么 i 就与 al 相等了),则直接将 a 与 b 当前遍历到的“祖宗”进行比较。
排序时检查重复
接下来,我们针对这样的情况进行优化:假设元素中不存在重复,那么我们就可以不执行后面的遍历并去除重复的操作了。实现如下:
$ = function() {
var hasDuplicate = true;
[0, 0].sort(function() {
hasDuplicate = false;
return 0;
});
var sort = function(a, b){
if (a === b) {
hasDuplicate = true;
return 0;
}
// Other Codes ...
};
var getParents = function(elem) {
// Other Codes ...
}
var siblingCheck = function(a, b, ret) {
// Other Codes ...
};
return {
unique : function(elems) {
elems.sort(sort);
if (hasDuplicate) {
for ( var i = 1; i < elems.length; i++ ) {
if ( elems[i] === elems[ i - 1 ] ) {
elems.splice( i--, 1 );
}
}
}
return elems;
}
}
}();
一个特殊的例外是某些浏览器可能进行了特殊的优化,那么在元素相等时可能就没有调用我们的排序函数了;这种情况下,排序时检查重复的方案就不可行。因此,如果浏览器在元素相等的情况下会调用我们的排序函数,那么就将 hasDuplicate 置为 false,并在排序过程中检查重复;否则,无论如何都视为存在重复,仍然进行遍历去除重复的操作。
使用compareDocumentPosition()进行优化
事实上,除 IE 之外浏览器都有内置的 compareDocumentPosition() 方法,用于比较两个 DOM Element 的位置,因此我们可以引入 compareDocumentPosition() 进行优化:
if (document.documentElement.compareDocumentPosition) {
var sort = function(a, b) {
if (a === b) {
hasDuplicate = true;
return 0;
}
if (!a.compareDocumentPosition || !b.compareDocumentPosition) {
return a.compareDocumentPosition ? -1 : 1;
}
return a.compareDocumentPosition(b) & 4 ? -1 : 1;
}
} else {
var sort = function(a, b) {
// Original implemention ...
}
}
a.compareDocumentPosition(b) & 4 表示取返回值的二进制表示的倒数第三位;compareDocumentPosition() 的返回值的每个二进制位表示不同的含义,其中倒数第三位表示元素 a 是否在元素 b 的 “前面”,这正是我们需要的。
最后的优化
最后,在sort的原始实现前插入一些代码,使得该函数有机会提前退出:
var sort = function(a, b){
var aParents = getParents(a),
bParents = getParents(b),
al = aParents.length,
bl = bParents.length,
ap = a.parentNode,
bp = b.parentNode,
i;
if (a === b) {
hasDuplicate = true;
return 0;
// If the nodes are siblings (or identical) we can do a quick check
} else if (ap === bp) {
return siblingCheck( a, b );
// If no parents were found then the nodes are disconnected
} else if (!ap) {
return -1;
} else if (!bp) {
return 1;
}
for (i = 0; i < al && i < bl; i++) {
ap = aParents[i];
bp = bParents[i];
if (ap !== bp) {
return siblingCheck(ap, bp);
}
}
return i === al ?
siblingCheck(a, bp, -1) :
siblingCheck(ap, b, 1);
};
以上就是 jQuery.unique() 的实现过程了。jQuery 的 unique() 在去除重复前先进行了排序,并使用了多种优化手段,实现了性能较好并且比较通用的去除重复的 DOM Element 的功能。
附件是本文中用到的一些代码。
分享到:
相关推荐
在本项目中,结合了 `jquery.plupload.queue` 插件,使得文件上传变得更加用户友好,可以实现文件的批量上传功能。 批量上传是指用户可以选择多个文件,一次性提交到服务器进行处理。`jquery.plupload.queue` 插件...
在网页设计和开发中,jQuery Accordion 是一种常见的交互元素,它通过折叠和展开的方式组织内容,使得用户能够更高效地浏览和导航。这个技术基于流行的JavaScript库jQuery,以其简洁的API和强大的功能赢得了开发者们...
jQuery.Callbacks是jquery在1.7版本之后加入的,是从1.6版中的_Deferred对象中抽离的,主要用来进行函数队列的add、remove、fire、lock等操作,并提供once、memory、unique、stopOnFalse四个option进行一些特殊的...
jQuery树形菜单是一种用于网页界面中的交互式菜单结构,它允许用户以层级方式展示数据,类似于计算机文件系统的目录结构。这种菜单通常用于网站导航、文件管理或其他需要展示分类信息的场景。jQuery 插件 `jquery....
它基于jQuery库,能够轻松地在网页上实现可交互的树形结构。 根据提供的文档信息,我们了解到一个具体的jQuery树形菜单插件——**jQuery Treeview**。该插件来源于Bassistance网站(...
除此之外,jQuery还提供了许多便利的工具方法,如"$().each()"遍历集合,"$().is()"检查元素是否匹配某个选择器,"$().data()"管理元素的数据存储,"$().unwrap()"移除元素的父元素,"$().unique()"去除重复的元素等...
本资源摘要信息汇总了前端JS面试题中的一些重要知识点,涵盖了JQuery选择器、JQuery插件实现方式、bind和live方法的区别、JS和JQ对象的转换、去除数组重复项等内容。 一、JQuery选择器 JQuery选择器是JQuery库中最...
总的来说,jQuery的`$.unique()`和`sort()`方法提供了一种方便的方式来处理数组的去重和排序需求。然而,了解JavaScript原生的数组方法以及它们的工作原理也是非常重要的,这可以帮助你更好地优化代码,特别是在不...
9. **性能优化**:jQuery提供了一些工具方法,如`.data()`, `.delegate()`, `.live()`, `.unique()`等,帮助开发者优化代码,提高页面性能。 通过深入学习jQuery源码,开发者不仅可以提升自身的技术水平,还能更好...
另外,jQuery.unique(array)方法是用于删除数组中重复元素的工具。它返回一个新数组,数组中的元素都是唯一的。这对于处理具有重复值的数组非常有帮助。 文章中提供的示例代码演示了如何利用上述方法来实现特定功能...
在客户端验证中,`jQuery Validate`是一个非常流行和强大的验证插件,它与后端技术如Java相结合,能实现高效且友好的验证机制。 `jQuery Validate`插件是由Jörn Zaefferer开发的,它是jQuery库的一个扩展,用于...
4. **样式和效果**:jQuery可以方便地改变元素的样式,如`hide()`和`show()`隐藏和显示元素,`fadeIn()`和`fadeOut()`实现淡入淡出效果。`addClass()`和`removeClass()`用来添加或移除类名。 5. **Ajax**:jQuery的`...
这份前端面试题资料涵盖了jQuery选择器、jQuery插件实现方式、jQuery事件绑定方法(bind与live的区别)、JavaScript数组去重、以及JavaScript与jQuery对象之间的转换等多个知识点。 ### jQuery选择器 jQuery提供了...
本教程将围绕“jQuery 横向纵向菜单”这一主题,详细介绍如何使用 jQuery 实现这种功能,并提供源代码供参考。 首先,让我们了解什么是横向和纵向菜单。横向菜单通常位于网页顶部,选项水平排列,而纵向菜单则常...
上述三种方法各有利弊,开发者可以根据具体需求和场景选择合适的实现方式。在使用这些方法时,需要注意输入字符串中可能存在的特殊情况,如特殊字符、空格等,这些因素都可能影响去重效果。此外,对于动态页面,如果...
在这个例子中,jQuery用于监听按钮点击事件,并通过`.fadeToggle()`方法结合CSS的透明度动画实现段落的淡入淡出效果。 总结,jQuery和CSS是构建现代网页的重要工具。jQuery简化了JavaScript操作,提高了开发效率,...
9. **实用函数**:jQuery 还提供了许多实用函数,如 `.data()`, `.val()`, `.toggle()`, `.clone()`, `.unwrap()`, `.unique()` 等,它们在处理数据、控制元素状态等方面非常有用。 10. **性能优化**:理解如何避免...
【ASP.NET中使用JqGrid完整实现】 JqGrid是一个强大的JavaScript控件,专用于显示和操作表格数据。它的特点是外观时尚,功能丰富,并且支持AJAX方式,使得数据加载和交互更加流畅。JqGrid是由Trirand公司的Tony ...