一个方法的产生通常是基于应用的。而jQuery的这个access()方法,我们相信一开始也是为了处理注入"attr","css","html","text"等方法的解耦性而设想的。我们都知道jQuery的一大特点:链式调用。而链式调用是一个公用特性,没有必要在“attr”,"css"等各个方法中都做处理,或者引用考虑链式的操作。而这个jQuery.access()方法的目的就是,由它来处理一些功能是否需要链式调用,而“attr”,"css",“html”等方法则专注于他们的功能实现上,不用考虑链式调用的相关问题。
再一个,举例"css"方法,我们大家熟悉的“css”方法设置属性的调用有以下几种:
$("div").css("position","absolute"); $("div").css({color:"red",border:"1px solid blue"}); $("div").css(width:function(index,value){ return parseFloat(value)*1.2+index; });
调用方式分为三种:
@1:(name,value),传入名称和值的方式
@2:({key:value,key:value}),传入一个键值对map对象。
@3:(name:fn(index,value){}),传入一个名称和一个运算函数,函数接收两个值:index-元素中集合中的序列值,value-元素原来的值。
而jQuery.access()方法的另一个目标,就是实现这三种传值方式的格式化,然后调用相应的功能方法。“attr”,"css","html"等方法,不用考虑这些传值的方式,他们需要采用第一种方式来接收处理传值即可,其他的事情就交给access方法来解决。
这样,我们就明白了“jQuery.access”方法存在的目的和意义了,有两个:一是处理链式调用的操作问题,二是处理传值方式的格式化问题。
怀揣着目标我们再去看"jQuery.access"方法的源码,就容易理解了。
这个“jQuery.access”是定义在“jQuery”对象上的静态方法,作为工具方法来使用的,而我们要得到的链式调用的返回对象是jquery包装的DOM集合,所以需要将包装的DOM元素传递进来。
第一个参数:elms。
而“atrr”,"css","html"等方法的功能处理是截然不同的,所以需要一个他们自己的功能方法的回调。
第二个参数:fn。
传递键值对map对象是一个参数,传递name-value,name-callback是两个参数,所以我们需要key,value连个参数。
第三个参数:key。
第四个参数:value。
"attr","css"等设置值时是返回jquery包装对象,可以链式调用的,获取属性或样式时,返回的是相关属性样式值,是不可继续链式调用的。所以需要一个是否链式调用的值。
第五个参数:chainable。
当我们用$("...")获取对象进行操作时,如果获取的对象个数为0时,我们通过“attr”,"css"获取属性样式时,应该返回什么值呢?比如,我们attr要返回null,而html需要返回“”空字符串,这就需要再来一个参数。
第六个参数:emptyGet。
jQuery中还有一个数据绑定的方法“$('...').data”中也使用了“access()”方法,而通过data方法,我们可以这样调用:
$("div").data("message":{name:"girl",age:"18"}); $("div").data("loaded","yes"); $("div").data("callback",function(){ .... ...... });
这里第三种写法的value值是一个函数,但是不需要它运行来返回属性值,它本身就是一个属性,需要保存的只是这个函数的一个引用,所以相比“attr”,"css"等方法,我们需要value是函数是,是否要运行,就要再来一个参数。
第七个参数:pass。
上面我们通过需求的方式分析了jQuery.access()方法的七个参数的作用。
access: function( elems, fn, key, value, chainable, emptyGet, pass ) { var exec, bulk = key == null, i = 0, length = elems.length; // 如果key是对象,那么就是{key:value}传值方式,通过迭代调用access来获取需要的输入方式。 if ( key && typeof key === "object" ) { for ( i in key ) { jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); } chainable = 1; // key不是对象,是(name,value)传值时。 } else if ( value !== undefined ) { //判断pass来获取exec标识符,决定value函数是否要执行。 exec = pass === undefined && jQuery.isFunction( value ); if ( bulk ) { // html(value)方法key为null,只有value时的处理。 if ( exec ) { exec = fn; fn = function( elem, key, value ) { return exec.call( jQuery( elem ), value ); }; // Otherwise they run against the entire set } else { fn.call( elems, value ); fn = null; } } //执行相应的功能处理回调。 if ( fn ) { for (; i < length; i++ ) { fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); } } //这里都为set方法,chainable为1。 chainable = 1; } //根据chainable返回是否链式调用 return chainable ? elems : // Gets,fn.call(elems)是html()方法获取内容,fn(elems[0],key)是其他方法获取属性值。 bulk ? fn.call( elems ) : length ? fn( elems[0], key ) : emptyGet; //emptyGet是元素个数为0时返回的空对象。 }
这时,我们再看jQuery.access()方法,就能很容易明白其中的逻辑了。
if ( key && typeof key === "object" ) { for ( i in key ) { jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); } chainable = 1; }
这一段,用来处理输入的{key:value,key:value}键值对设值对象的参数标准化,通过迭代递归调用来处理。
else if ( value !== undefined ) { exec = pass === undefined && jQuery.isFunction( value ); }
用来判断这个value函数是否要执行,data()函数中传入该值为false,其他功能函数都不传此值。
if ( bulk ) { // html(value)方法key为null,只有value时的处理。 if ( exec ) { exec = fn; fn = function( elem, key, value ) { return exec.call( jQuery( elem ), value ); }; // Otherwise they run against the entire set } else { fn.call( elems, value ); fn = null; } }
这一段代码,来处理html(value)这种没有key值,单值设置的value值。
html: function( value ) { return jQuery.access( this, function( value ) { var elem = this[0] || {}, i = 0, l = this.length; ....... ..... }, null, value, arguments.length ); }
jQuery.fn.extend({ attr: function( name, value ) { return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); }, ...... }); jQuery.extend({ attr: function( elem, name, value, pass ) { var ret, hooks, notxml, nType = elem.nodeType; // don't get/set attributes on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } ..... });
对比上面html方法和attr方法所需参数的不同,attr等方法的dom元素是通过参数传递的,而html方法的dom参数是通过this[0]获取的。
而bulk代码里面通过fn.call(elm,value)就是针对html()方法传递dom元素和单value值的。
其中的exec判断,并且包装了fn方法,是为了下面先运行value的求值函数,然后再赋值,同时也是跟其他传递dom参数的函数调用形式上进行同化。value不是函数的话,就将fn=null避免下面代码导致重复运行。
chainable = 1;
这一句表示作为set函数设置值时,是可以链式调用的。我认为这行代码多余,因为“attr”,"css"等方法都通过“arguments.length”传递了该值。
if ( fn ) { for (; i < length; i++ ) { fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); } }
这段代码是用来进行对元素集合中的每个dom元素进行set设置属性值,
exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value
这句代码是判断value是否是函数,是的话就先执行,再传递值。通过call的方式调用,那么这个value函数中的this就是元素本身了。value函数还传递了两个参数:
@1:“i”-元素的集合中的序列。
@2:“fn(elems[i],key)”-通过fn函数的获取值的方法得到元素对应属性的值。也是递归调用了。“attr”,“css”等方法不传value值时就可以作为get获取值的方法运行。
pass还是要传递的,这个还是对data()函数的处理。
return chainable ? elems : bulk ? fn.call( elems ) : length ? fn( elems[0], key ) : emptyGet;
最后return的代码分成连部分,chainable?为真时,返回elms元素,用做链式调用。chainable?为假时,进行get求值运算,并返回。
bulk为真时,表示没有key值,运行“fn.call(elms)”针对html()单参数的方法。bulk为假时,判断元素个数,有元素,则获取第一个元素的相关属性值,没有元素则返回null或者“”、{}(这个根据功能函数调用access()方法传递的emptyGet参数决定)。
聪明的你可能会疑问,那处理html()方法的调用怎么没有判断元素个数,你细看看上面html()方法中的代码:“var elem=this[0] || {}",它本身自己已经做了元素为空的处理了,所以这里不用判断元素个数。
通过本文的分析和详解,你可能觉得jQuery.access()方法功能这么强大,能包装这么多功能函数,怎么没出现在API中呢?其实你也发现了,这个方法虽然强大,但是使用限制太多,参数太多,用起来太麻烦,而且稍有不慎就会引起报错。所以只能作为内部方法使用,不适合作为API工具功能使用。