当前位置: 代码迷 >> Web前端 >> 躺在jquery的石榴裙上 之 clean
  详细解决方案

躺在jquery的石榴裙上 之 clean

热度:408   发布时间:2012-10-31 14:37:32.0
躺在jquery的石榴裙下 之 clean
接上一节的两个函数

selector = jQuery.clean( [ match[1] ], context );
jQuery( context ).find( selector );

clean (约939),find (约)

clean: function( elems, context ) 

elems : 数组
context : 上下文

var ret = [];
...
return ret;

那么ret就相当于是创建好的dom数组

context = context || document;

这种写法经常出现,允许参数缺省

if (typeof context.createElement == 'undefined')
	context = context.ownerDocument || context[0] && context[0].ownerDocument || document;

这句主要是处理浏览器兼容型问题,因为在ie下context.createElement会打印"object",而不是"function",context.ownerDocument通过字面上意思可以得出是获取document,但出过来的context也可能是window对象,所以就有后面||的故事了。对于为什么不直接写document,我的理解是他可能是想对xml也添加支持,所以在大师脚下一定不要有自以为是这种想法,这是很危险的,我们除了敬仰就剩下仰望了,保持一颗虔诚的心,来看下面的代码。

下面是一段jQuery.each(elems, function(i, elem),然后就直接return ret了

先来看看jQuery.each代码
each: function( object, callback, args ) {
	var name, i = 0, length = object.length;
	if ( args ) {
		if ( length == undefined ) {
          ....//1
			break;
		} else
          ....//2
			break;
	} else {
		if ( length == undefined ) {
          ....//3
			break;
		} else
          ...//4
	}

	return object;
}

args是用来供内部使用的,我来从1,2,3,4一个一个分析
//1 存在args,但是没有length属性object
for ( name in object )
	if ( callback.apply( object[ name ], args ) === false )
		break;

遍历object里面的属性然后执行callback方法,而且将args注入进来,当返回值为false的情况下终止循环,注意是用的"===",返回0,"false"是无效的
//2 和 //1一样,只是他是一个含有length的object,便利的方式不同而已,毕竟for要比foreach速度上要快
//3 外部使用,无length属性
for ( name in object )
	if ( callback.call( object[ name ], name, object[ name ] ) === false )
		break;
这个就看的很清楚了,当我们使用
$.each(["aa","bb"],function(i,n){ alert(n == this)}) //true
就可以知道其原因了
//4
for ( var value = object[0];i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
看似好像和前面不一样,其实这只是一种幻觉,他只是把工作全部转移到for里面去了,充分的利用了语法,可读性下去了,性能没测不好评论,不过这种花式写法我还是推崇的,毕竟写代码是个枯燥无味的,有时候能利用智慧玩点花样找点乐子,也是一种生活态度


言归正传,继续clean
jQuery.each(elems, function(i, elem){
    if ( !elem ) return; //elem为null继续下一个循环
     if ( elem.constructor == Number ) elem += ''; //将数字变为字符串
     ....
    //form和select都有length
    if ( elem.length === 0 && (!jQuery.nodeName( elem, "form" ) && !jQuery.nodeName( elem, "select" )) )
	return; //并且不是form和select,继续下一个循环
    if ( elem[0] == undefined || jQuery.nodeName( elem, "form" ) || elem.options )//假设它不是一个类数组了或者它是一个form或select
	ret.push( elem );
    else
         //它就是一个数组
	ret = jQuery.merge( ret, elem );
}


nodeName: function( elem, name ) {
	return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
}

//向first上追加second
merge: function( first, second ) {
	var i = 0, elem, pos = first.length;
	if ( jQuery.browser.msie ) {
		while ( elem = second[ i++ ] )
			if ( elem.nodeType != 8 )
				first[ pos++ ] = elem;

	} else
		while ( elem = second[ i++ ] )
			first[ pos++ ] = elem;

	return first;
}


现在来分析上面那个...段的代码
elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag) {
  return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
    all :
    front + "></" + tag + ">";
                    }); 


试验这样一段代码 : $("<input type=\"text\" value=\"haha\" />")
下面使用alert打印的结果
all: <input type="text" value="哈哈"/>
front: <input type="text" value="哈哈"
tag: input

其实本意目的就是将<div/> 改为<div></div>

//去空白创建,临时div
var tags = jQuery.trim(elem).toLowerCase(), div = context.createElement("div");

var wrap =
!tags.indexOf("<opt") &&
[1, "<select multiple='multiple'>", "</select>"] || 

!tags.indexOf("<leg") &&
[1, "<fieldset>", "</fieldset>"] || 

tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
[1, "<table>", "</table>"] || 

!tags.indexOf("<tr") &&
[2, "<table><tbody>", "</tbody></table>"] || 

(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
[3, "<table><tbody><tr>", "</tr></tbody></table>"] || 

!tags.indexOf("<col") &&
[2, "<table><tbody></tbody><colgroup>", "</colgroup></table>"] || 

jQuery.browser.msie &&
[1, "div<div>", "</div>"] || 

[0, "", ""]; 

建立了一个数组,这个数组时干嘛的呢.就比如说opt必须是放在select地下,而leg必须放在fieldset底下,前面的数字代表深度

div.innerHTML = wrap[1] + elem + wrap[2]; //不解释

//通过深度得到最内层的元素,如"<table><tbody><tr>", "</tr></tbody></table>"得到的就是里面的最里面td
while (wrap[0]--)
	div = div.lastChild; 
if (jQuery.browser.msie) { 
	var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ?
	div.firstChild && div.firstChild.childNodes : 
	wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ?
		div.childNodes :
		[]; 
	for (var j = tbody.length - 1; j >= 0; --j)
		if (jQuery.nodeName(tbody[j], "tbody") && !tbody[j].childNodes.length)
		tbody[j].parentNode.removeChild(tbody[j]); 
      //ie当使用innerHTML的时候会自动去掉空白节点,下面就是将其恢复
	if (/^\s/.test(elem))
		div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]), div.firstChild); 
} 

可见深度主要是针对ie兼容性的问题,ie又一次的擅住主张,自动加入tbody,jquery作者真是煞费苦心啊
  相关解决方案