jQuery源码历代记1传送门 http://adamed.iteye.com/blog/1426963
前面详述了jQuery构造器中size和get的源码。
下面介绍下一个构造器中的方法each:
each : function( fn , args ) { return jQuery.each( this , fn , args ); }
从源码中可以看出该方法就是直接调用jQuery工具方法each。
我们先简单看下这个方法(jQuery工具方法后在后面专门篇章进行详解,但由于jQuery.each()在很多地方使用到,所以再此先对其进行详解):
each : function( obj , fn , args ) { if ( obj.length == undefined ){ for ( var i in obj ){ fn.apply( obj[ i ] , args || [ i , obj[ i ] ] ); } }else{ for ( var i = 0; i < obj.length; i++ ){ fn.apply( obj[ i ] , args || [ i , obj[ i ] ] ); } } return obj; }
这段代码很简洁,作用也很明显就是对obj集合中的每一个对象执行fn方法。
我们先看一下API中对jQuery.each(collection, callback(indexInArray, valueOfElement) )方法的描述:
Description: A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. Arrays and array-like objects with a length property (such as a function's arguments object) are iterated by numeric index, from 0 to length-1. Other objects are iterated via their named properties.
简单翻译一下:这是一个通用的迭代函数,它可以用来无缝迭代数组及对象。数组以及带有length属性这样的准数组对象(例如function中的arguments对象)是通过索引数值进行迭代的(从0到length-1)。其他对象是通过他们的属性名进行迭代的。
前面已经说过了,理解一个function设计原理先看它的使用方法。
对于jQuery.each()的实现源码的理解要从这个方法的使用开始入手,还是看API:
如果jQuery.each()方法的第一个参数是一个数组,则回调function的第一个参数是数组的索引。
$.each([52, 97], function(index, value) { alert(index + ': ' + value); });//得到结果:0:52 1:97
若jQuery.each()方法的第一个参数传递的是Js对象(类似于JSON对象)则回调function的第一个参数是对象的属性名:
var map = {'flammable': 'inflammable','duh': 'no duh'}; $.each(map, function(key, value) {alert(key + ': ' + value);}); //得到结果:flammable:inflammable duh:no duh
PS:我们可以通过在回调函数中return false;来结束each方法的循环过程。return 任何非false的值均相当于执行continue,立刻进入下一个循环执行的过程。(1.2以后的版本的功能,参考下面的源码)
好了,明白了使用方法以后我们看下jQuery构造器的each方法中最最关键也是最可能不明白的一句代码:
fn.apply( obj[ i ] , args || [ i , obj[ i ] ] );
为了容易理解,我们拆分下API源码并举例:
var tempFunction = function(index, value) { alert(index + ': ' + value); };
$.each([52, 97], tempFunction);
调用each方法时执行的是:
for ( var i in obj ){ fn.apply( obj[ i ] , args || [ i , obj[ i ] ] ); }
我们以第一次迭代为例,i=0 obj为[52, 97],obj[i]为52(注意这里是1个对象)
循环体执行时相当于tempFunction.apply(52,[0,52]) 也就是相当于执行 52.tempFunction(0,52)。所以在tempFunction中获取this时拿到的是52.
参考API中的描述:
Description: In the case of an array, the callback is passed an array index and a corresponding array value each time. (The value can also be accessed through the this keyword, but Javascript will always wrap the this value as an Object even if it is a simple string or number value.) The method returns its first argument, the object that was iterated.
简单翻译就是例子中的52在调用tempFunction时先被封装成了Object。(^_^翻译的简单吧。)
//********************************我是成长的分界线*****************************
jQuery的each方法在jQuery 1.2以后实现方式重构为(为了理解方便我添加了一些大括号):
each: function( object, callback, args ) { var name, i = 0, length = object.length; if ( args ) { //*****这里省略了一些代码****** } else { if ( length == undefined ) { for ( name in object ){ if ( callback. call( object[ name ], name, object[ name ] ) === false ){ break; } } } else{ for ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){ //do nothing } } return object; }
其实只是把apply改成call了,数组for循环时把执行那个地方放在判断区了。。核心思想没啥变化。。
//*******************************这是传送门分割线******************************
我们回到jQuery 1.0构造器each方法这边来。
通过return jQuery.each( this , fn , args );我们看出来jQuery对象的each只是迭代jQuery对象本身的各个属性。