首页 > 代码库 > JQuery源码解析-JQuery的工具方法(4)
JQuery源码解析-JQuery的工具方法(4)
下面对这些方法进行讲解
each():遍历集合
trim():去前后空格
makeArray():类数组转换真数组
inArray():数组版indexOf
merge():合并数组
grep():过滤新数组
map():映射新数组
each方法:
each是遍历集合用的方法,也是一个加强版的循环操作。例如:
var arr = { Name: ‘jam‘, Age: 15 }; $.each(arr, function (key, val) { console.log(key + ":" + val); //Name:jam Age:15 })
each方法不仅可以对数组,类数组进行遍历,还可以对json对象进行遍历。源码:
// args is for internal usage only each: function( obj, callback, args ) { var value, i = 0, length = obj.length, isArray = isArraylike( obj ); if ( args ) { if ( isArray ) { for ( ; i < length; i++ ) { value = callback.apply( obj[ i ], args ); if ( value =http://www.mamicode.com/== false ) { break; } } } else { for ( i in obj ) { value = callback.apply( obj[ i ], args ); if ( value =http://www.mamicode.com/== false ) { break; } } } // A special, fast, case for the most common use of each } else { if ( isArray ) { for ( ; i < length; i++ ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value =http://www.mamicode.com/== false ) { break; } } } else { for ( i in obj ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value =http://www.mamicode.com/== false ) { break; } } } } return obj; },
可以看到这个方法,实际接收了三个参数,最后一个参数为内部使用,我们在外部使用的时候只需要两个参数就够了。
首先第一个部分是声明内部用到的变量:
var value, i = 0, length = obj.length, isArray = isArraylike( obj );
前三个参数没什么说的,主要看一下isArray这个变量,通过isArraylike这个方法,可以判断是否为数组或类数组,如果是,则返回true。
接下来进行判断args,用来区分是否为内部调用:
if ( args ) {//源码} else {//源码}
先看一下满足条件里面的源码,也就是内部使用的都做了什么:
if ( isArray ) { for ( ; i < length; i++ ) { value = callback.apply( obj[ i ], args ); if ( value =http://www.mamicode.com/== false ) { break; } } } else { for ( i in obj ) { value = callback.apply( obj[ i ], args ); if ( value =http://www.mamicode.com/== false ) { break; } } }
先看传入的对象是否为数组或类数组,如果是则遍历这个对象,然后将当前项和参数传入回调方法,进行回调。
如果不满足条件,那么就是json对象,所以进行for in遍历,然后执行相同的操作,这里看到回调方法会返回一个值,如果为false,则跳出循环,所以如果想则each方法跳出循环是,直接return false;就可以了
接下来看一下我们平时使用的代码:
if ( isArray ) { for ( ; i < length; i++ ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value =http://www.mamicode.com/== false ) { break; } } } else { for ( i in obj ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value =http://www.mamicode.com/== false ) { break; } } }
代码和上面内部使用的没有太大的区别,只不过调用回调方法时传入的参数不一样,另外这个方法使用了call,内部方法使用的是apply,也就是说在jQuery内部,传入的参数个数可能不一样,而我们使用时,只需要传入两个就可以了。
trim方法:
trim 方法是对字符串去前后空格的方法,在平时经常使用这个方法,源码:
trim: function( text ) { return text == null ? "" : core_trim.call( text ); },
源码很简单,如果传入的字符串为null或undefined,则直接返回空字符就可以了,如果有值,则直接调用es5的trim方法就行。这个core_trim指向的是原生的trim方法。
core_trim = core_version.trim,
makeArray方法:
这个方法可以将类数组转换成真正的数组,方法有两个参数,如果传入第二个参数,则返回类数组。
页面中有三个div,分别是1、2、3
window.onload = function () { var divs = document.getElementsByTagName(‘div‘); console.log($.makeArray(divs));//[div, div, div] }
传入第二个参数时:
window.onload = function () { var divs = document.getElementsByTagName(‘div‘); console.log($.makeArray(divs, { length: 1 }));//Object {1: div, 2: div, 3: div, length: 4} }
下面看一下源码:
// results is for internal usage only makeArray: function( arr, results ) { var ret = results || []; if ( arr != null ) { if ( isArraylike( Object(arr) ) ) { jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr ); } else { core_push.call( ret, arr ); } } return ret; },
首先检查是否传入第二个参数,如果为空,则给一个空数组。然后查看参数是否为数组或类数组。注意这里:
if ( isArraylike( Object(arr) ) ) {
这里将参数外面包一层object,是将参数转换为对象,如果传入数字,则返回false,如果传入的是字符串,那么会返回true,因为字符串被转成对象了,并且有长度。
然后通过merge方法进行附加,把传入的参数合并到ret中。
如果不为数组或类数组,则直接用数组的push将参数附加到数组中即可。
inArray方法:
这个方法和数组的indexof功能一样,返回查找值的索引:
var arr = [‘a,‘, ‘b‘, ‘c‘]; console.log($.inArray(‘c‘,arr,0)); //2
源码如下:
inArray: function( elem, arr, i ) { return arr == null ? -1 : core_indexOf.call( arr, elem, i ); },
源码很简单,第一个参数是要查找的值,第二个是对象,第三个是从第几项开始查找。然后调用数组的indexOf方法。
merge方法:
这个方法经常用到,可以将两个数组合并成一个。
var arr1 = [‘a‘, ‘b‘]; var arr2 = [‘c‘, ‘d‘]; var arr3 = $.merge(arr1, arr2); console.log(arr3);//["a", "b", "c", "d"]
另外第二个参数还可以传入一个类数组。
var arr1 = [‘a‘, ‘b‘]; var arr2 = { 0: ‘c‘, 1: ‘d‘}; var arr3 = $.merge(arr1, arr2); console.log(arr3);//["a", "b", "c", "d"]
当第一个参数为类数组时:
var arr1 = [‘a‘, ‘b‘]; var arr2 = { 0: ‘c‘, 1: ‘d‘ }; var arr3 = $.merge(arr2, arr1); console.log(arr3);////Object {0: "c", 1: "d", 2: "a", 3: "b", length: 4}
看到,当第一个参数为类数组时,返回的也就类数组的形式。
源码如下:
merge: function( first, second ) { var l = second.length, i = first.length, j = 0; if ( typeof l === "number" ) { for ( ; j < l; j++ ) { first[ i++ ] = second[ j ]; } } else { while ( second[j] !== undefined ) { first[ i++ ] = second[ j++ ]; } } first.length = i; return first; },
先获取两个参数的长度,如果第二个参数有值,并且是数字类型的话,那么直接遍历第二个数组,附加到第一个上即可。
如果第二个参数没有长度,也就是可能是json对象,那么直接遍历,这里需要注意,即使是json对象,那么里面的属性名也必须是1、2、3。例如:
var arr2 = { 0: ‘c‘, 1: ‘d‘ };
最后更新长度,并返回合并后的对象。
grep方法:
grep方法是对数组进行筛选,只返回满足条件的值。例如:
var arr = [1, 2, 3, 4, 5]; arr=$.grep(arr,function(v,i){ return v>2; }); console.log(arr);//[3, 4, 5]
另外这个方法还接受第三个参数,如果传入,那么代表筛选条件进行反转。例如上面的例子中,如果传入第三个参数,那么筛选的条件实际业绩刘是v<=2。
源码:
grep: function( elems, callback, inv ) { var retVal, ret = [], i = 0, length = elems.length; inv = !!inv; // Go through the array, only saving the items // that pass the validator function for ( ; i < length; i++ ) { retVal = !!callback( elems[ i ], i ); if ( inv !== retVal ) { ret.push( elems[ i ] ); } } return ret; },
先对判断第三个参数是否传入,如果没传,那么!!inv则为false。然后遍历传入的对象,并将每项都传入回调方法中,最后利用inv进行控制,当inv为false的时候,也就是没传入第三个参数时,
那么回调函数返回结果为true时,则添加到返回的ret中,这里用到:
if ( inv !== retVal ) { ret.push( elems[ i ] ); }
这个判断,使代码非常灵活。
map方法,这个方法是返回操作后的一个新数组,例如:
var arr = [1, 2, 3, 4, 5]; arr=$.map(arr,function(v,i){ return v * 2; }); console.log(arr);//[2, 4, 6, 8, 10]
使用很简单,来看下源码:
// arg is for internal usage only map: function( elems, callback, arg ) { var value, i = 0, length = elems.length, isArray = isArraylike( elems ), ret = []; // Go through the array, translating each of the items to their if ( isArray ) { for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret[ ret.length ] = value; } } // Go through every key on the object, } else { for ( i in elems ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret[ ret.length ] = value; } } } // Flatten any nested arrays return core_concat.apply( [], ret ); },
方法接收三个参数,第三个参数是jQuery内部使用的,平时一般用不到。
接下来判断传入的参数是否为数组或类数组,如果是:
for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret[ ret.length ] = value; } }
遍历这个对象,并将每项传入回调方法中,最后将回调方法的返回值放入定义好的ret中。
如果不是数组或类数组:
for ( i in elems ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret[ ret.length ] = value; } }
利用for in 循环这个对象每项,并将每项传入回调方法中,最后将回调方法的返回值放入定义好的ret中。
最后返回这个新对象:
// Flatten any nested arrays return core_concat.apply( [], ret );
可以看到这里使用到了数组的concat方法,而不是直接返回ret,这是因为这个方法不想返回嵌套数组的结构,例如:
var arr = [1, 2, 3, 4, 5]; arr=$.map(arr,function(v,i){ return [v * 2]; }); console.log(arr);//[2, 4, 6, 8, 10]
如果回调方法返回的就是数组,那么经过这个处理还是返回一个数组的结构,如果将源码中的最后一句替换掉:
// Flatten any nested arrays //return core_concat.apply( [], ret ); return ret;
那么返回的是:[[2], [4], [6], [8], [10]]这种形式了。
JQuery源码解析-JQuery的工具方法(4)