`
裴小星
  • 浏览: 265831 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
8ccf5db2-0d60-335f-a337-3c30d2feabdb
Java NIO翻译
浏览量:27853
F3e939f0-dc16-3d6e-8c0b-3315c810fb91
PureJS开发过程详解
浏览量:74380
07a6d496-dc19-3c71-92cf-92edb5203cef
MongoDB Java ...
浏览量:63081
社区版块
存档分类
最新评论

jQuery.unique()的实现方式

阅读更多
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 的功能。
  附件是本文中用到的一些代码。
18
17
分享到:
评论

相关推荐

    Plupload + jquery.plupload.queue 批量上传 和 plupload.full单个上传

    在本项目中,结合了 `jquery.plupload.queue` 插件,使得文件上传变得更加用户友好,可以实现文件的批量上传功能。 批量上传是指用户可以选择多个文件,一次性提交到服务器进行处理。`jquery.plupload.queue` 插件...

    jquery accordion 菜单折叠效果

    在网页设计和开发中,jQuery Accordion 是一种常见的交互元素,它通过折叠和展开的方式组织内容,使得用户能够更高效地浏览和导航。这个技术基于流行的JavaScript库jQuery,以其简洁的API和强大的功能赢得了开发者们...

    jquery.Callbacks的实现详解

    jQuery.Callbacks是jquery在1.7版本之后加入的,是从1.6版中的_Deferred对象中抽离的,主要用来进行函数队列的add、remove、fire、lock等操作,并提供once、memory、unique、stopOnFalse四个option进行一些特殊的...

    jQuery树形菜单显示

    jQuery树形菜单是一种用于网页界面中的交互式菜单结构,它允许用户以层级方式展示数据,类似于计算机文件系统的目录结构。这种菜单通常用于网站导航、文件管理或其他需要展示分类信息的场景。jQuery 插件 `jquery....

    jQuery树形菜单.doc

    它基于jQuery库,能够轻松地在网页上实现可交互的树形结构。 根据提供的文档信息,我们了解到一个具体的jQuery树形菜单插件——**jQuery Treeview**。该插件来源于Bassistance网站(...

    jQueryAPI-090129.rar jQueryAPI-090129文档

    除此之外,jQuery还提供了许多便利的工具方法,如"$().each()"遍历集合,"$().is()"检查元素是否匹配某个选择器,"$().data()"管理元素的数据存储,"$().unwrap()"移除元素的父元素,"$().unique()"去除重复的元素等...

    前端js面试题WORD.docx

    本资源摘要信息汇总了前端JS面试题中的一些重要知识点,涵盖了JQuery选择器、JQuery插件实现方式、bind和live方法的区别、JS和JQ对象的转换、去除数组重复项等内容。 一、JQuery选择器 JQuery选择器是JQuery库中最...

    jQuery简单实现对数组去重及排序操作实例

    总的来说,jQuery的`$.unique()`和`sort()`方法提供了一种方便的方式来处理数组的去重和排序需求。然而,了解JavaScript原生的数组方法以及它们的工作原理也是非常重要的,这可以帮助你更好地优化代码,特别是在不...

    锋利的jQuery 源码

    9. **性能优化**:jQuery提供了一些工具方法,如`.data()`, `.delegate()`, `.live()`, `.unique()`等,帮助开发者优化代码,提高页面性能。 通过深入学习jQuery源码,开发者不仅可以提升自身的技术水平,还能更好...

    利用jQuery操作对象数组的实现代码

    另外,jQuery.unique(array)方法是用于删除数组中重复元素的工具。它返回一个新数组,数组中的元素都是唯一的。这对于处理具有重复值的数组非常有帮助。 文章中提供的示例代码演示了如何利用上述方法来实现特定功能...

    ,jQuery 进阶学习.pptx )

    合理评估每种方法的优缺点,根据实际需求选择合适的实现方式。 **示例**:为`&lt;a&gt;`元素绑定点击事件处理函数时,可以考虑使用原生JavaScript的方法,如`addEventListener`,而不是jQuery的`on`方法。 通过上述内容...

    java使用jquery_validate插件实现文本校验

    在客户端验证中,`jQuery Validate`是一个非常流行和强大的验证插件,它与后端技术如Java相结合,能实现高效且友好的验证机制。 `jQuery Validate`插件是由Jörn Zaefferer开发的,它是jQuery库的一个扩展,用于...

    jQuery1.4.1中文参考(chm)

    4. **样式和效果**:jQuery可以方便地改变元素的样式,如`hide()`和`show()`隐藏和显示元素,`fadeIn()`和`fadeOut()`实现淡入淡出效果。`addClass()`和`removeClass()`用来添加或移除类名。 5. **Ajax**:jQuery的`...

    前端面试题全套整理附详细解答.pdf

    这份前端面试题资料涵盖了jQuery选择器、jQuery插件实现方式、jQuery事件绑定方法(bind与live的区别)、JavaScript数组去重、以及JavaScript与jQuery对象之间的转换等多个知识点。 ### jQuery选择器 jQuery提供了...

    jquery横向纵向菜单

    本教程将围绕“jQuery 横向纵向菜单”这一主题,详细介绍如何使用 jQuery 实现这种功能,并提供源代码供参考。 首先,让我们了解什么是横向和纵向菜单。横向菜单通常位于网页顶部,选项水平排列,而纵向菜单则常...

    jquery实现去除重复字符串的方法小结

    上述三种方法各有利弊,开发者可以根据具体需求和场景选择合适的实现方式。在使用这些方法时,需要注意输入字符串中可能存在的特殊情况,如特殊字符、空格等,这些因素都可能影响去重效果。此外,对于动态页面,如果...

    jquery和css

    在这个例子中,jQuery用于监听按钮点击事件,并通过`.fadeToggle()`方法结合CSS的透明度动画实现段落的淡入淡出效果。 总结,jQuery和CSS是构建现代网页的重要工具。jQuery简化了JavaScript操作,提高了开发效率,...

    JQUERY 应用实例

    9. **实用函数**:jQuery 还提供了许多实用函数,如 `.data()`, `.val()`, `.toggle()`, `.clone()`, `.unwrap()`, `.unique()` 等,它们在处理数据、控制元素状态等方面非常有用。 10. **性能优化**:理解如何避免...

    ASP.NET中使用JqGrid完整实现.docx

    【ASP.NET中使用JqGrid完整实现】 JqGrid是一个强大的JavaScript控件,专用于显示和操作表格数据。它的特点是外观时尚,功能丰富,并且支持AJAX方式,使得数据加载和交互更加流畅。JqGrid是由Trirand公司的Tony ...

Global site tag (gtag.js) - Google Analytics