jQuery技术内幕:深入解析jQuery架构设计与实现原理. 3.10 工具方法

简介:

3.10 工具方法

3.10.1 Sizzle.uniqueSort( results )

工具方法Sizzle.uniqueSort( results )负责对元素集合中的元素按照出现在文档中的顺序进行排序,并删除重复元素。

相关代码如下所示:

4026 Sizzle.uniqueSort = function( results ) {

4027     if ( sortOrder ) {

4028         hasDuplicate = baseHasDuplicate;

4029         results.sort( sortOrder );

4030

4031         if ( hasDuplicate ) {

4032             for ( var i = 1; i < results.length; i++ ) {

4033                 if ( results[i] === results[ i - 1 ] ) {

4034                     results.splice( i--, 1 );

4035                 }

4036             }

4037         }

4038     }

4039

4040     return results;

4041 };

第4029行:调用数组方法sort()对数组中的元素进行排序。其中,sortOrder( a, b )是比较函数,负责比较元素a和元素b在文档中的位置。如果比较函数sortOrder( a, b )遇到相等的元素,即重复元素,会设置变量hasDuplicate为true。关于比较函数sortOrder( a, b )的具体说明请参见3.10.2节。

第4031~4037行:如果变量hasDuplicate为true,表示存在重复元素,则遍历数组results,比较相邻元素是否相等,如果相等则删除。

第4028行:开始排序和去重时,先设置变量hasDuplicate的默认值为变量baseHas

Duplicate,变量baseHasDuplicate指示了JavaScript引擎在排序时是否会进行优化。

相关代码如下所示:

3864     hasDuplicate = false,

3865     baseHasDuplicate = true,

 

3870 // Here we check if the JavaScript engine is using some sort of

3871 // optimization where it does not always call our comparision

3872 // function. If that is the case, discard the hasDuplicate value.

3873 // Thus far that includes Google Chrome.

3874 [0, 0].sort(function() {

3875     baseHasDuplicate = false;

3876     return 0;

3877 });

第3874~3877行:检查JavaScript引擎在排序时是否会进行优化。在早期的Chrome浏览器中,排序时如果遇到相等的元素,不会调用比较函数,新版本中已经取消了这一优化。如果遇到相等元素便不调用比较函数,此时变量baseHasDuplicate默认为true,即只能假设数组中含有重复元素;如果遇到相等元素时仍然会调用比较函数,则变量baseHasDuplicate将被设置为false,这种情况下需要在比较函数中判断是否含有重复元素。

读者可以访问http://bugs.jquery.com/ticket/5380查看该bug的描述。

3.10.2 sortOrder( a, b )

函数sortOrder( a, b )负责比较元素a和元素b在文档中的位置。如果元素a在元素b之前,则返回-1;如果元素a在元素b之后,则返回1;如果元素a与元素b相等,则返回0。

函数sortOrder( a, b )通过调用原生方法compareDocumentPosition()或比较原生属性source

Index来实现。原生方法compareDocumentPosition()用于比较两个元素的文档位置;原生属性sourceIndex则返回元素在文档中的序号,返回值等于该元素在document.getElementsBy

TagName('*')返回的数组中的下标。更多信息请访问http://www.quirksmode.org/dom/w3c_core.html。

函数sortOrder( a, b )执行的3个关键步骤如下:

1)如果浏览器支持原生方法compareDocumentPosition(),则调用该方法比较元素位置。

2)如果浏览器支持原生属性sourceIndex,则用该属性比较元素位置。

3)否则比较祖先元素的文档位置。

下面来看看该函数的源码实现。

1.?浏览器支持原生方法compareDocumentPosition()的情况

如果浏览器支持原生方法compareDocumentPosition(),则调用该方法比较元素位置。相关代码如下所示:

4805 var sortOrder, siblingCheck;

4806

4807 if ( document.documentElement.compareDocumentPosition ) {

4808     sortOrder = function( a, b ) {

4809        if ( a === b ) {

4810            hasDuplicate = true;

4811            return 0;

4812        }

4813

4814        if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {

4815           return a.compareDocumentPosition ? -1 : 1;

4816        }

4817

4818        return a.compareDocumentPosition(b) & 4 ? -1 : 1;

4819     };

4820

2.?浏览器支持原生属性sourceIndex的情况

如果浏览器支持原生属性sourceIndex,则用该属性比较元素位置。

相关代码如下所示:

4821 } else {

4822     sortOrder = function( a, b ) {

4823         // The nodes are identical, we can exit early

4824         if ( a === b ) {

4825            hasDuplicate = true;

4826            return 0;

4827

4828         // Fallback to using sourceIndex (in IE) if it's available on both nodes

4829         } else if ( a.sourceIndex && b.sourceIndex ) {

4830             return a.sourceIndex - b.sourceIndex;

4831         }

4832

3.否则比较祖先元素的文档位置

(1)元素a和元素b是兄弟元素的情况

相关代码如下所示:

4833         var al, bl,

4834             ap = [],

4835             bp = [],

4836             aup = a.parentNode,

4837             bup = b.parentNode,

4838             cur = aup;

4839

4840         // If the nodes are siblings (or identical) we can do a quick check

4841         if ( aup === bup ) {

4842             return siblingCheck( a, b );

4843

第4836~4837行、第4841~4842行:变量aup是元素a的父元素,变量bup是元素b的父元素。如果变量aup与变量bup相等,说明元素a和元素b是兄弟元素,则调用函数siblingCheck()比较元素a和元素b的文档位置。函数siblingCheck( a, b, ret )负责比较兄弟元素的文档位置,稍后会看到该函数的源码实现和分析。

(2)没有找到父元素的情况

相关代码如下所示:

4844         // If no parents were found then the nodes are disconnected

4845         } else if ( !aup ) {

4846             return -1;

4847

4848         } else if ( !bup ) {

4849             return 1;

4850         }

4851

第4845~4850行:如果元素a没有父元素,则认为元素a不在文档中,返回-1,元素a将排在元素b之前;如果元素b没有父元素,则认为元素b不在文档中,返回1,元素b将排在元素a之前。

(3)查找元素a和元素b的祖先元素

相关代码如下所示:

4852         // Otherwise they're somewhere else in the tree so we need

4853         // to build up a full list of the parentNodes for comparison

4854         while ( cur ) {

4855             ap.unshift( cur );

4856             cur = cur.parentNode;

4857         }

4858

4859         cur = bup;

4860

4861         while ( cur ) {

4862             bp.unshift( cur );

4863             cur = cur.parentNode;

4864         }

4865

(4)比较祖先元素的文档位置

相关代码如下所示:

4866         al = ap.length;

4867         bl = bp.length;

4868

4869         // Start walking down the tree looking for a discrepancy

4870         for ( var i = 0; i < al && i < bl; i++ ) {

4871             if ( ap[i] !== bp[i] ) {

4872                 return siblingCheck( ap[i], bp[i] );

4873             }

4874         }

4875

第4866~4874行:从最顶层的祖先元素开始向下遍历,如果祖先元素ap[i]与bp[i]不是同一个元素,那么它们必然是兄弟元素,此时可以通过比较两个祖先元素的文档位置,来确定元素a和元素b的相对位置。

(5)元素a和元素b的文档深度不一致的情况

相关代码如下所示:

4876         // We ended someplace up the tree so do a sibling check

4877         return i === al ?

4878             siblingCheck( a, bp[i], -1 ) :

4879             siblingCheck( ap[i], b, 1 );

4880     };

4881

第4877~4878行:如果元素a的文档深度较小,此时元素a与元素b的祖先元素bp[i]要么是兄弟元素,要么是同一个元素,可以调用函数siblingCheck( a, b, ret )比较文档位置。

第4877~4879行:如果元素b的文档深度较小,此时元素a的祖先元素ap[i]与元素b要么是兄弟元素,要么是同一个元素,可以调用函数siblingCheck( a, b, ret )比较文档位置。

(6)siblingCheck( a, b, ret )

函数siblingCheck( a, b, ret )负责比较兄弟元素的文档位置。该函数从元素a向后遍历(nextSibling),如果遇到元素b,说明元素a在元素b之前,则返回-1;如果一直没遇到,说明元素a在元素b之后,则返回1。

相关代码如下所示:

4882     siblingCheck = function( a, b, ret ) {

4883         if ( a === b ) {

4884             return ret;

4885         }

4886

4887         var cur = a.nextSibling;

4888

4889         while ( cur ) {

4890             if ( cur === b ) {

4891                 return -1;

4892             }

4893

4894             cur = cur.nextSibling;

4895         }

4896

4897         return 1;

4898     };

4899 }

3.10.3 Sizzle.contains( a, b )

工具方法Sizzle.contains( a, b )负责检测元素a是否包含元素b。该方法通过调用原生方法contains()或compareDocumentPosition()实现。原生方法contains()用于检测一个元素是否包含另一个元素;原生方法compareDocumentPosition()用于比较两个元素的文档位置,更多信息请访问以下网址:

http://ejohn.org/blog/comparing-document-position/

http://www.quirksmode.org/dom/w3c_core.html#miscellaneous

相关代码如下所示:

5242 if ( document.documentElement.contains ) {

5243     Sizzle.contains = function( a, b ) {

5244        return a !== b && (a.contains ? a.contains(b) : true);

5245     };

5246

5247 } else if ( document.documentElement.compareDocumentPosition ) {

5248     Sizzle.contains = function( a, b ) {

5249        return !!(a.compareDocumentPosition(b) & 16);

5250     };

5251

5252 } else {

5253     Sizzle.contains = function() {

5254        return false;

5255     };

5256 }

3.10.4 Sizzle.error( msg )

工具方法Sizzle.error( msg )用于抛出一个含有选择器表达式语法错误信息的异常。

相关代码如下所示:

4178 Sizzle.error = function( msg ) {

4179     throw new Error( "Syntax error, unrecognized expression: " + msg );

4180 };

3.10.5 Sizzle.getText( elem )

工具方法Sizzle.getText( elem )用于获取元素集合中所有元素合并后的文本内容。

相关代码如下所示:

4182 /**

4183 * Utility function for retreiving the text value of an array of DOM nodes

4184 * @param {Array|Element} elem

4185 */

4186 var getText = Sizzle.getText = function( elem ) {

4187     var i, node,

4188         nodeType = elem.nodeType,

4189         ret = "";

4190

4191     if ( nodeType ) {

4192         if ( nodeType === 1 || nodeType === 9 ) {

4193             // Use textContent || innerText for elements

4194             if ( typeof elem.textContent === 'string' ) {

4195                 return elem.textContent;

4196             } else if ( typeof elem.innerText === 'string' ) {

4197                 // Replace IE's carriage returns

4198                 return elem.innerText.replace( rReturn, '' );

4199             } else {

4200                 // Traverse it's children

4201                 for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {

4202                     ret += getText( elem );

4203                 }

4204             }

4205         } else if ( nodeType === 3 || nodeType === 4 ) {

4206             return elem.nodeValue;

4207         }

4208     } else {

4209

4210         // If no nodeType, this is expected to be an array

4211         for ( i = 0; (node = elem[i]); i++ ) {

4212             // Do not traverse comment nodes

4213             if ( node.nodeType !== 8 ) {

4214                 ret += getText( node );

4215             }

4216         }

4217     }

4218     return ret;

4219 };

第4191~4207行:如果参数elem是元素,则尝试读取属性textContent或innerText,如果不支持则遍历子元素,递归调用工具函数getText( elem )来获取每个子元素的文本内容,并合并;如果参数elem是Text节点或CDATASection节点,则直接返回节点值nodeValue。

第4208~4217行:否则认为参数elem是元素集合,遍历该元素集合,递归调用函数getText(elem)获取每个元素的文本内容,并合并。

第4218行:最后返回合并后的文本内容。

相关文章
|
4天前
|
设计模式 Cloud Native 算法
拥抱变化:我的技术适应之旅构建未来:云原生架构在企业数字化转型中的关键角色
【4月更文挑战第30天】 在技术的浪潮中,我学会了不仅仅是编码,还有如何与时俱进。本文记录了我从一名初出茅庐的开发者成长为一个能够适应不断变化技术环境的工程师的心路历程。从最初的困惑与挑战到后来的接纳与创新,我意识到,技术能力的提升和心态的转变同样重要。
|
4天前
|
前端开发 JavaScript 安全
【TypeScript技术专栏】TypeScript在微前端架构中的应用
【4月更文挑战第30天】微前端架构通过拆分应用提升开发效率和降低维护成本,TypeScript作为静态类型语言,以其类型安全、代码智能提示和重构支持强化这一架构。在实践中,TypeScript定义公共接口确保跨微前端通信一致性,用于编写微前端以保证代码质量,且能无缝集成到构建流程中。在微前端架构中,TypeScript是保障正确性和可维护性的有力工具。
|
4天前
项目管理工具计划模板解析:项目管理工具的双重功能与创建方法
本文介绍了项目计划模板的含义和重要性。项目计划模板是用于规划项目结构的可编辑文档,帮助团队明确任务、分配责任和管理时间。模板有助于跟踪项目进度、避免任务冲突,并简化会议安排。创建模板通常涉及选择合适的项目管理工具,如Zoho Projects或Microsoft Excel,然后分解任务、定义日期并持续调整。在Zoho Projects中,用户可以按步骤创建模板,包括命名、添加任务和设置相关细节。
13 0
|
4天前
|
负载均衡 Java 开发者
Spring Cloud:一文读懂其原理与架构
Spring Cloud 是一套微服务解决方案,它整合了Netflix公司的多个开源框架,简化了分布式系统开发。Spring Cloud 提供了服务注册与发现、配置中心、消息总线、负载均衡、熔断机制等工具,让开发者可以快速地构建一些常见的微服务架构。
|
4天前
|
JavaScript 前端开发 开发者
Node.js的包管理和npm工具深度解析
【4月更文挑战第30天】本文深入解析Node.js的包管理和npm工具。包管理促进代码复用和社区协作,包包含元数据描述文件`package.json`和入口文件。npm提供搜索、安装、发布等功能,通过命令行进行操作,如`install`、`search`、`uninstall`。npm支持版本控制、全局安装、脚本定义及私有仓库。理解和熟练运用npm能提升Node.js开发效率。
|
6天前
|
设计模式 供应链 安全
如何在短频快的节奏中做好技术?业务开发必会的架构思维
本文提供一种业务架构设计模式:从业务&技术两个角度提炼出一个基础思维框架,供业务线开发同学参考。
如何在短频快的节奏中做好技术?业务开发必会的架构思维
|
10天前
|
Cloud Native Devops 持续交付
构建未来:云原生技术在现代IT架构中的演进与实践
【4月更文挑战第24天】 随着企业数字化转型的深入,云原生技术正成为推动创新和敏捷性的关键技术。本文聚焦于云原生技术的发展历程及其在现代IT架构中的应用,探讨了如何利用容器化、微服务、DevOps和持续集成/持续部署(CI/CD)等核心概念来优化资源利用率,提高系统弹性,并加速产品上市时间。通过分析多个行业案例,文章揭示了云原生实践对企业竞争力的显著影响,并提出了面向未来的IT架构战略建议。
|
12天前
|
SQL 存储 关系型数据库
数据库开发之图形化工具以及表操作的详细解析
数据库开发之图形化工具以及表操作的详细解析
28 0
|
16天前
|
机器学习/深度学习 语音技术 网络架构
【视频】LSTM神经网络架构和原理及其在Python中的预测应用|数据分享
【视频】LSTM神经网络架构和原理及其在Python中的预测应用|数据分享
25 0

推荐镜像

更多