`

jQuery乱谈(三)

 
阅读更多

 昨天说到了jQuery()的实现,还差一处理HTML字符串的部分没有分析完,今天继续。 

if ( typeof selector === "string" ) {
            if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
                // Assume that strings that start and end with <> are HTML and skip the regex check
                match = [ null, selector, null ];
            } else {
                match = rquickExpr.exec( selector );
            }
            // Match html or make sure no context is specified for #id
            if ( match && (match[1] || !context) ) {
                // HANDLE: $(html) -> $(array)
                if ( match[1] ) {
                    context = context instanceof jQuery ? context[0] : context;
                    doc = ( context && context.nodeType ? context.ownerDocument || context : document );
                    // scripts is true for back-compat
                    selector = jQuery.parseHTML( match[1], doc, true );
                    if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
                        this.attr.call( selector, context, true );
                    }
                    return jQuery.merge( this, selector );
                // HANDLE: $(#id)
                } else {
                    elem = document.getElementById( match[2] );
                    // Check parentNode to catch when Blackberry 4.6 returns
                    // nodes that are no longer in the document #6963
                    if ( elem && elem.parentNode ) {
                        // Handle the case where IE and Opera return items
                        // by name instead of ID
                        if ( elem.id !== match[2] ) {
                            return rootjQuery.find( selector );
                        }
                        // Otherwise, we inject the element directly into the jQuery object
                        this.length = 1;
                        this[0] = elem;
                    }
                    this.context = document;
                    this.selector = selector;
                    return this;
                }
            // HANDLE: $(expr, $(...))
            } else if ( !context || context.jquery ) {
                return ( context || rootjQuery ).find( selector );
            // HANDLE: $(expr, context)
            // (which is just equivalent to: $(context).find(expr)
            } else {
                return this.constructor( context ).find( selector );
            }
        }

  下面我们来一段一段分析:

if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
          // Assume that strings that start and end with <> are HTML and skip the regex check
          match = [ null, selector, null ];
  } else {
        match = rquickExpr.exec( selector );
  }

  selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3这一句用于判断selector字符串是否由"<"开始,">"结尾,而且selector字符串的长度不小于3.就是类似于:$('<p id="test">My <em>new</em> text</p>')这样的情况。如果selector是html标签组成的话,直接match = [ null, selector, null ];而不用正则检查。否则的话需要match = rquickExpr.exec( selector );。rquickExpr变量是在前面定义的一个正则式字面量:rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/。这段正则式只是一种检查HTML字符串的简便方式优先检测#id,防止XSS通过location.hash攻击,利用检查正则表达式HTML字符串还是元素ID字符串match = rquickExpr.exec( selector )这一句把字符串中的匹配正则表达式的文本保存到match数组里。这里用到了我们前面提到的正则表达式quickExpr,match其实是一个数组,第0个元素是与正则表达式相匹配的文本,第1个元素是与正则表达式的第1个子表达式相匹配的文本(如果有的话),第2个元素是第2个子表达式相匹配的文本(如果有的话),第3个元素是第3个子表达式相匹配的文本(如果有的话),这里就是元素的ID,不包含#。

  注:XSS攻击请参看:http://baike.baidu.com/view/2161269.htmexec()方法请参看:http://www.w3school.com.cn/js/jsref_exec_regexp.asp

  后面的一部分我将以注释的形式解释:      

   /**

        *正则表达式匹配到了内容,并且 match[1]不为空,或者context为空。

        *match[1]不为空的时候selector是HTML字符串,也就是你可以用$("xland")把对象包装成jQuery对象        

        *context为空的时候selector是页面元素ID

        */

      if ( match && (match[1] || !context) ) {
                // 处理$(html) -> $(array),选择器为html字符串
                if ( match[1] ) {
                    context = context instanceof jQuery ? context[0] : context; // 不好意思,这句话我也不理解
            /**
             * ownerDocument属性返回节点所属的根元素。下面的JS语句保证了上下文(作用域)为根节点
             *
*/ doc
= ( context && context.nodeType ? context.ownerDocument || context : document );

            /**
             *
jQuery.parseHTML方法接受三个参数(data, context, scripts)。data表示HTML字符串;            
* context表示是作用域,默认是document,可选;scripts
* 是一个boolean值,如果是true的话,就会把HTML字符串里面的脚本也正确解析
             *
             */
            selector = jQuery.parseHTML( match[1], doc, true );
                    if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
                        this.attr.call( selector, context, true );
                    }
                    return jQuery.merge( this, selector );
                // 选择器为ID,处理形如$("#first")
                } else {
                    elem = document.getElementById( match[2] ); //match[2]得到ID的值如:first
                    if ( elem && elem.parentNode ) {
                        if ( elem.id !== match[2] ) {
                            return rootjQuery.find( selector ); // 找到元素
                        }
                        // 否则直接把元素插入到爆jquery对象
                        this.length = 1;
                        this[0] = elem;
                    }
                    this.context = document;
                    this.selector = selector;
                    return this;
                }
            // 处理形如$("div.container")的表达式字符串
            } else if ( !context || context.jquery ) {
                return ( context || rootjQuery ).find( selector );
            // 处理形如$("div.container",$('#asd'))的表达式字符串,
// 相当于$(context).find(expr) ,就是在context上下文中查找
       } else {
           return this.constructor( context ).find( selector );
      }

  jQuery核心函数分析完毕,感觉还是有很多不满意的地方,慢慢改正吧。

--------------------------------------------添加---------------------------------------------------

  jQuery核心函数里面涉及到了merge()、find()函数。我简单解释一下这俩货吧,先说merge():

merge: function( first, second ) {
        var l = second.length,
            i = first.length,
            j = 0;

        if ( typeof l === "number" ) {
            for ( ; j < l; j++ ) {
                first[ i++ ] = second[ j ];
            }

        } else {
            while ( second[j] !== undefined ) {
                first[ i++ ] = second[ j++ ];
            }
        }

        first.length = i;

        return first;
    }

  该方法用于合并两个jQuery对象(因为jQuery对象中有length属性)或者数组内容到第一个对象或数组。使用方法为:jQuery.merge( first, second )。其中first数组是用来合并的数组或jQuery对象,元素是从第二数组或jQuery对象加进来的;second数组或jQuery对象合并到第一,保持不变。内部代码比较简单,主要是判断提供的第二个参数是jQuery对象(因为jQuery对象中有length属性)还是数组其中:

if ( typeof l === "number" ) {
            for ( ; j < l; j++ ) {
                first[ i++ ] = second[ j ];
            }

        }

  当第二个参数的length属性是number类型时,直接循环遍历把第二个参数的每一项添加到第一个参数的后面。接着:

else {
            while ( second[j] !== undefined ) {
                first[ i++ ] = second[ j++ ];
            }
        }

  当第二个参数的length属性不是number类型时,循环把第二个参数的每一项添加到第一个参数的后面,一直到第二个参数的一项为undefined,也就是没有定义时结束。

  find()方法代码如下:

find: function( selector ) {
        var i, l, length, n, r, ret,
            self = this;
        // 处理jQuery对象或DOM元素
        if ( typeof selector !== "string" ) {
            return jQuery( selector ).filter(function() {
                for ( i = 0, l = self.length; i < l; i++ ) {
                    if ( jQuery.contains( self[ i ], this ) ) {
                        return true;
                    }
                }
            });
        }
        // 把DOM元素集合加入到jQuery堆栈中
        ret = this.pushStack( "", "find", selector );
        //  处理选择器字符串,循环把选择的元素添加到堆栈
        for ( i = 0, l = this.length; i < l; i++ ) {
            length = ret.length;
            jQuery.find( selector, this[i], ret );

            if ( i > 0 ) {
                // Make sure that the results are unique
                for ( n = length; n < ret.length; n++ ) {
                    for ( r = 0; r < length; r++ ) {
                        if ( ret[r] === ret[n] ) {
                            ret.splice(n--, 1);
                            break;
                        }
                    }
                }
            }
        }

        return ret;
    }

  该方法可以获得当前元素匹配集合中每个元素的后代,选择性筛选的选择器。使用方法如下:

  如果一个jQuery对象表示一个DOM元素的集合,.find()方法允许我们能够通过搜索DOM树中的这些元素的后代元素从匹配的元素构造一个新的jQuery象。find()children()方法是相似的,但后者只是旅行的DOM树向下一个层级(就是只查找子元素,而不是后代元素)。该方法选择性地接受同一类型选择表达,我们可以传递给$()函数。如果紧随兄弟匹配选择器,它在新建成的jQuery对象中留下;否则,它被排除在外。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics