测试代码1值access与普通对象的关系:
<div id="test">111</div>
<div id="child">1111111</div>
var access1 = jQuery.access1 = function( elems, fn, key, value, chainable, emptyGet, raw ) {
>ar i = 0,
length = elems.length,
//第三个参数是可选的,通过它走不同的逻辑
ulk = key == null;
// alert(value)
// Sets many values
//如果第三个是[object Object]对象
if ( jQuery.type( key ) === "object" ) {
//表示链式调用,也就是回归调用access
chainable = true;
for ( i in key ) {
//对第三个参数迭代出键名
Query.access1( elems, fn, i, key[i], true, emptyGet, raw );
}
// Sets one value
//第二次迭代出object的"xxx","female"
//第一次回归调用的时候value是"xxx"
} else if ( value !== undefined ) {
chainable = true;
//这一次不是value不是函数,是"xxx"
if ( !jQuery.isFunction( value ) ) {
raw = true;
}
//key是name,所以bulk是false,这里的if不会执行!bulk用于判断是否有键名!
if ( bulk ) {
// Bulk operations run against the entire set
//最后一个参数raw
if ( raw ) {
fn.call( elems, value );
n = null;
// ...except when executing function values
} else {
bulk = fn;
fn = function( elem, key, value ) {
return bulk.call( jQuery( elem ), value );
};
}
}
//第二个参数是fn,就是回调函数
if ( fn ) {
//length就是选择器的DOM的个数,这里就是$("div").length,逐个调用fn函数,传入的参数是$("div")[i]
//第二个参数就是迭代出来的key这里"name","sex",如果raw是true那么就是第三个参数是"xxx","female"
//否则就是把这个value作为函数调用!
for ( ; i < length; i++ ) {
//第一次因为传入的是object对象,第一次递归调用时候key是"name",value是"xxx",来到这里length是2,所以调用两次
//第二次递归调用时候key是sex,value是female,同时length是2,所以在这里又调用两次,打印结果如下:
//[testnamexxx,object childnamexxx,object testsexfemale,childsexfemale]
//也就是说$("div")[0]会拿着传入的{name:"xxx",sex:"female"}对象调用两次,第一次传入的参数是name,xxx,第二次传入的参数是sex,female
fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
}
}
}
//打印[true,true,true]表示第一次递归调用过后是true,第二次递归调用也是true,一开始的那一次也是true
// alert(chainable);
return chainable ?
//因为三次的chainable都是true,所以三次返回的都是elems,因为递归过程中第一个参数一直是没有变化的,所以是$("div")
elems :
// Gets
bulk ?
fn.call( elems ) :
length ? fn( elems[0], key ) : emptyGet;
};
var elements=$("div");
var key={name:"xxx",sex:"female"}
//回调函数
var f=function(){alert(arguments[0].id+arguments[1]+arguments[2]);}
//打印"test",也就是返回值是$("div")对象!
alert(access1(elements,f,key)[0].id);
测试代码2之text方法与access关系:(html调用方式一样只是回调函数不一样)
var access1 = jQuery.access1 = function( elems, fn, key, value, chainable, emptyGet, raw ) {
var i = 0,
length = elems.length,
//第三个参数是可选的,通过它走不同的逻辑
bulk = key == null;
// alert(value)
// Sets many values
//如果第三个是[object Object]对象
if ( jQuery.type( key ) === "object" ) {
//表示链式调用,也就是回归调用access
chainable = true;
or ( i in key ) {
//对第三个参数迭代出键名
jQuery.access1( elems, fn, i, key[i], true, emptyGet, raw );
}
// Sets one value
//第二次迭代出object的"xxx","female"
//第一次回归调用的时候value是"xxx"
} else if ( value !== undefined ) {
chainable = true;
//这一次不是value不是函数,是"xxx"
if ( !jQuery.isFunction( value ) ) {
raw = true;
}
//key是name,所以bulk是false,这里的if不会执行!bulk用于判断是否有键名!
if ( bulk ) {
// Bulk operations run against the entire set
//最后一个参数raw
if ( raw ) {
fn.call( elems, value );
fn = null;
// ...except when executing function values
} else {
//text方法走了这里的逻辑:bulk赋值为函数fn,同时fn设置为一个新的函数,形成闭包,在该fn里面调用bulk函数的,第一个是上下文this
//绑定到jQuery(elem)上面
bulk = fn;
fn = function( elem, key, value ) {
return bulk.call( jQuery( elem ), value );
};
}
}
//第二个参数是fn,就是回调函数
if ( fn ) {
for ( ; i < length; i++ ) {
fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
//text方法走到这里的时候,给fn传入了三个参数,分别是this[i],key=null,raw为false,第三个参数无法调用因为null无法调用
//底层调用bulk方法,给bulk方法传入参数,分别为jQuery(this[i]),也就是外层的调用对象的第i个DOM对象,value是null
}
}
}
//打印[true,true,true]表示第一次递归调用过后是true,第二次递归调用也是true,一开始的那一次也是true
// alert(chainable);
return chainable ?
//因为三次的chainable都是true,所以三次返回的都是elems,因为递归过程中第一个参数一直是没有变化的,所以是$("div")
elems :
// Gets
bulk ?
fn.call( elems ) :
length ? fn( elems[0], key ) : emptyGet;
};
jQuery.fn.extend({
text1:function( value ) {
//是否是根据是否传入参数判断是读取函数设置参数!
return jQuery.access1( this, function( value ) {
//在access里面调用了该函数,其中this是jQuery(elem[i])表示上下文,第二个参数value传入到这里,是"先谢谢"
//这里是打印n1,因为上面已经封装为jQuery对象了,所以用attr方法调用!
//alert(this.attr("id"));
//alert(value===undefined);这个条件只有在调用方式为:$("div").text1()时候满足!
return value === undefined ?
jQuery.text( this ) ://jQuery.text = Sizzle.getText;调用Sizzle的getText方法
//将这个id是n1的对象清空,添加"先谢谢"
this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
//传入的参数为"先谢谢","1"。在access1中key是null导致access里面bulk是true,value是"先谢谢",raw是undefined
}, null, value, arguments.length );
}
})
alert($("div").text1());//从返回值可以知道这是由用的是jQuery.text(this)的逻辑,打印出[1111,111]
$("div").text1("先谢谢");//这是设置值
测试代码3之access与attr("name","xx")的关系:(prop方法是一样的)
var access1 = jQuery.access1 = function( elems, fn, key, value, chainable, emptyGet, raw ) {
var i = 0,
//length为2
length = elems.length,
//bulk为false
bulk = key == null;
if ( jQuery.type( key ) === "object" ) {
chainable = true;
for ( i in key ) {
jQuery.access1( elems, fn, i, key[i], true, emptyGet, raw );
}
} else if ( value !== undefined ) {
//chainable是true
chainable = true;
if ( !jQuery.isFunction( value ) ) {
raw = true;
}
if ( bulk ) {
// Bulk operations run against the entire set
//最后一个参数raw
if ( raw ) {
fn.call( elems, value );
fn = null;
// ...except when executing function values
} else {
bulk = fn;
fn = function( elem, key, value ) {
return bulk.call( jQuery( elem ), value );
};
}
}
//给所有的elems逐个调用fn函数,也就是jQuery.attr函数,传入两个参数,第一个是$("div")[i],第二个是"name",第三个是"Hello",因为raw是true
if ( fn ) {
for ( ; i < length; i++ ) {
fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
}
}
}
return chainable ?
//返回$("div")
elems :
// Gets
bulk ?
fn.call( elems ) :
length ? fn( elems[0], key ) : emptyGet;
};
jQuery.fn.extend({
attr1: function( name, value ) {
//调用access1方法时候前5个参数都有值,第五个是true
return access1( this, jQuery.attr, name, value, arguments.length > 1 );
}
})
//打印2,返回$("div")这是jQuery支持链式调用的关键!
alert($("div").attr1("name","Hello").length);
下面是jQuery.attr函数源码:
// Hook for boolean attributes
boolHook = {
set: function( elem, value, name ) {
if ( value === false ) {
// Remove boolean attributes when set to false
jQuery.removeAttr( elem, name );
} else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
// IE<8 needs the *property* name
elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
// Use defaultChecked and defaultSelected for oldIE
} else {
elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
}
return name;
}
};
// Use this for any attribute in IE6/7
// This fixes almost every IE6/7 issue
nodeHook = {
set: function( elem, value, name ) {
// Set the existing or create a new attribute node
var ret = elem.getAttributeNode( name );
if ( !ret ) {
elem.setAttributeNode(
(ret = elem.ownerDocument.createAttribute( name ))
);
}
ret.value = value += "";
// Break association with cloned elements by also using setAttribute (#9646)
if ( name === "value" || value === elem.getAttribute( name ) ) {
return value;
}
}
};
这里是jQuery.attr源码:
attr: function( elem, name, value ) {
var hooks, ret,
nType = elem.nodeType;
// don't get/set attributes on text, comment and attribute nodes
//不再text,注释,属性节点上添加属性
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
// Fallback to prop when attributes are not supported
//如果不支持getAttribute那么用jQuery.prop函数
if ( typeof elem.getAttribute === strundefined ) {
return jQuery.prop( elem, name, value );
}
// All attributes are lowercase
// Grab necessary hook if one is defined
//如果不是Element或者也不是XMLDoc文档
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
//属性名变成小写
name = name.toLowerCase();
//hooks
//如果存在jQuery.attrHooks["name"]或者name满足下面的正则表达式设置为boolHook,否则为nodeHook
///^(?:checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$/i
hooks = jQuery.attrHooks[ name ] ||
( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
}
//存在value值,attr("name","xxxx")
if ( value !== undefined ) {
//如果value是null,注意这里是===不是==所以必须手动传递null
//这时候就会把elem上面的相应属性移除
if ( value === null ) {
jQuery.removeAttr( elem, name );
} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
//存在属性值最终调用setAttribute方法
elem.setAttribute( name, value + "" );
return value;
}
//如果不存在属性值
} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
return ret;
} else {
//调用jQuery.find.attr方法
//jQuery.find = Sizzle;调用Sizzle的attr方法,传入参数为元素和属性名
ret = jQuery.find.attr( elem, name );
// Non-existent attributes return null, we normalize to undefined
//如果没有找到这个属性值,那么返回undefined,否则返回属性值
return ret == null ?
undefined :
ret;
}
}
下面是access函数与html()源码,access运行逻辑和text一样只是回调函数不一样:
<pre name="code" class="html">html: function( value ) {
return access( this, function( value ) {
//获取this[0]对象
var elem = this[ 0 ] || {},
i = 0,
l = this.length;
//如果不传value值,那么就是获取值
if ( value === undefined ) {
return elem.nodeType === 1 ?
//rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g
elem.innerHTML.replace( rinlinejQuery, "" ) :
undefined;
}
// See if we can take a shortcut and just use innerHTML
//如果value是string,不是script,style,link,如果支持
//rnoInnerhtml = /<(?:script|style|link)/i,
//rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i")
//rleadingWhitespace = /^\s+/,
//rtagName = /<([\w:]+)/,
//rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
( support.htmlSerialize || !rnoshimcache.test( value ) ) &&
( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
!wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) {
//对符合条件的进行替换
value = value.replace( rxhtmlTag, "<$1></$2>" );
try {
for (; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
elem = this[i] || {};
//如果是{}那么没有nodeType是undefined,之所以用这种方式是为了防止直接用undefined调用nodeType抛出异常
if ( elem.nodeType === 1 ) {
//清除数据防止内存泄漏,也就是用$.data保存的数据
jQuery.cleanData( getAll( elem, false ) );
//设置为value值
elem.innerHTML = value;
}
}
elem = 0;
// If using innerHTML throws an exception, use the fallback method
} catch(e) {}
}
//如果添加的是比如script或者link,style那么直接清空,然后把标签添加进去,但是必须调用转义!<script><\/script>
//结束标签如果没有转义,那么抛出uncaught SyntaxError.如果不是这些特殊的标签,我们也清空,然后把数据添加进去!
if ( elem ) {
this.empty().append( value );
}
}, null, value, arguments.length );
}
正则表达式测试1:
<pre name="code" class="html"><pre name="code" class="html">var rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g
/*该正则表达式前面有一个空格!*/
alert(rinlinejQuery.test(' jQuery112="123"'));
//这个正则表达式后面有一个g,所以第二个是false
alert(rinlinejQuery.test(' jQuery112="null"'));
var rinlinejQuery = / jQuery\d+="(?:null|\d+)"/
/*该正则表达式前面有一个空格!*/
alert(rinlinejQuery.test(' jQuery112="123"'));
//这里没有g,所以第二个也输出true,你你弄懂原因了吗微笑
alert(rinlinejQuery.test(' jQuery112="null"'));
<div id="n1">我是 jQuery12="null" xxx</div>
var rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g;
alert($("#n1")[0].innerHTML.replace(rinlinejQuery,""));//打印"我是 xxx",也就是中间满足正则表达式部分被忽略了!,同时jQuery前面要空格。用innerHTML会产生一个负作用,就是如果在id是n1的元素内如果有空格,那么IE7/8会忽略空格,但是其它浏览器不会,于是当html方法的结果用于if判断时候要手动清除空格!参见博客
</pre>正则表达式测试2:</p><p><pre name="code" class="html"><pre name="code" class="html">var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video";
//RegExp需要双重转义!
var rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i");
//都是打印true
alert(rnoshimcache.test("<video />"));
alert(rnoshimcache.test("<video/>"));
$.support.leadingWhitespace//表示使用innerHTML赋值时候时报保留前面的空白符!
var rleadingWhitespace = /^\s+/;//恰面有空格
正则表达式测试3:
//这是匹配所有的HTML标签前面的部分,如<html,<body
var rtagName = /<([\w:]+)/;
wrapMap = {
option: [ 1, "<select multiple='multiple'>", "</select>" ],
legend: [ 1, "<fieldset>", "</fieldset>" ],
area: [ 1, "<map>", "</map>" ],
param: [ 1, "<object>", "</object>" ],
thead: [ 1, "<table>", "</table>" ],
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
_default: $.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ]
}
//rtagName表示只能是\w或者:
//匹配所有的标签
alert(rtagName.test("<table>"));
alert(rtagName.test("<:>"));
//打印<table ,table
alert(rtagName.exec("<table></table>"));
//<legend,legend
alert(rtagName.exec("<legend></legend>"));
//[1,<filedSet>,<filedSet/>]
alert(wrapMap[rtagName.exec("<legend></legend>")[1]]);
//打印<filedset>
alert(wrapMap[rtagName.exec("<legend></legend>")[1]][1]);
正则表达式测试4:
var reg1=/Windows(?!95|98|NT|2000)/gi;
//可以匹配Windows,打印Windows
alert(reg1.exec("Windows4"));
//因为这后面有g,表示全局查找,通过打印RegExp的lastIndex就可以知道第一个匹配到的是第一个<字符,第二次匹配到的是结束标签的<字符!
//打印1,19
var rxhtmlTag1 = /<(?!area|br|col|embed|hr|img|input|link|meta|param)/gi;
var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
alert(rxhtmlTag1.exec("<table>I am testing</table>"));
alert(rxhtmlTag1.lastIndex);
alert(rxhtmlTag1.exec("<area>I am testing</area>"));
alert(rxhtmlTag1.lastIndex);
//下面测试rxhtmlTag正则表达式,下面就是null
alert(rxhtmlTag.exec("<table></table>"));
其它正则表达式测试:
(<span style="color:#ff0000;">?=pattern):</span>正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
<span style="color:#ff0000;">(?:pattern):</span>匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。
(<span style="color:#ff0000;">?<=pattern)</span>:反向肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能
匹配“3.1Windows”中的“Windows”。
<span style="color:#ff0000;">(?<!pattern):</span>反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。
<pre name="code" class="html">