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

    xiaoxiao2023-12-19  155

    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行:最后返回合并后的文本内容。

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)