测试代码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 ); }
<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">