昨天说到了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.htm; exec()方法请参看: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对象中留下;否则,它被排除在外。
共同学习,写下你的评论
评论加载中...
作者其他优质文章