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 === false ) {
                        break;
                    }
                }
            } else {
                for ( i in obj ) {
                    value = callback.apply( obj[ i ], args );

                    if ( value === 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 === false ) {
                        break;
                    }
                }
            } else {
                for ( i in obj ) {
                    value = callback.call( obj[ i ], i, obj[ i ] );

                    if ( value === 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 === false ) {
                        break;
                    }
                }
            } else {
                for ( i in obj ) {
                    value = callback.apply( obj[ i ], args );

                    if ( value === 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 === false ) {
                        break;
                    }
                }
            } else {
                for ( i in obj ) {
                    value = callback.call( obj[ i ], i, obj[ i ] );

                    if ( value === 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]]这种形式了。

  

原文地址:https://www.cnblogs.com/y8932809/p/5892056.html