• jQuery源码分析之jQuery工具方法


    //...... 省略代码
    var    //...... 省略代码
            trimLeft = /^s+/,
            trimRight = /s+$/,
          //...... 省略代码
          //_jQuery/_$分别保存全局变量jQuery/$,以防命名冲突
            _jQuery = window.jQuery,
            _$ = window.$,
          //...... 省略代码
          //缓存常用对象的方法为局部变量,提高访问效率
            toString = Object.prototype.toString,
            hasOwn = Object.prototype.hasOwnProperty,
            push = Array.prototype.push,
            slice = Array.prototype.slice,
            trim = String.prototype.trim,
            indexOf = Array.prototype.indexOf,
            class2type = {};
    //...... 省略代码
    jQuery.extend({
               //解决命名冲突,将原先_$/_jQuery重新赋值给全局变量$/jQuery,并返回jQuery的本地实现。
               //调用该方法后,外部不能通过$/jQuery名称访问jQuery的本地实现,只能用该函数返回值进行访问。
               //此方法的妙处在于命名冲突时,可将返回值赋值给外部的变量,再继续访问jQuery的本地实现。
                noConflict: function( deep ) {
                        if ( window.$ === jQuery ) {
                            window.$ = _$;
                        }
                        if ( deep && window.jQuery === jQuery ) {
                            window.jQuery = _jQuery;
                        }
                        return jQuery;
                },
             //获取obj的数据类型
             //检查对象数据类型可用typeof操作符,但typeof得到的类型值不靠谱,比如Date/RegExp对象的结果为object,而不是date/regExp。
             //这里是调用toString(Object.prototype.toString)获取对象的类型信息,这样较为妥当。也是建议的写法。
             //Date对象,调用toString方法后返回字符串[object Date];
             //RegExp对象,调用toString方法后返回字符串[object RegExp]。
             //变量class2type存储的key值为toString返回的字符串值(比如[object Function]),value值为类型名称(比如function)。
             //可通过class2type[toString.call(obj)]获取对象obj的类型。
                type: function( obj ) {
                        return obj == null ? String( obj ) : class2type[ toString.call(obj) ] || "object";
                },
                isFunction: function( obj ) {
                        return jQuery.type(obj) === "function";
                },
                //检测浏览器是否已实现Array.isArray,如是则无需再次实现。
                isArray: Array.isArray || function( obj ) {
                    return jQuery.type(obj) === "array";
                },
                isWindow: function( obj ) {
                    return obj && typeof obj === "object" && "setInterval" in obj;
                },
                isNumeric: function( obj ) {
                    return !isNaN( parseFloat(obj) ) && isFinite( obj );
                },
              //判断是否普通对象。以下为普通对象
              //var obj = new Object();obj.XXX = XXXX;
              //var obj = {XXX:XXXX};
                isPlainObject: function( obj ) {
               //非普通对象
               //值为''/0/false/null/undefined
               //非Object类型数据(RegExp,Date,Array,Function对象不属于Object类型的哦)
               //DOM对象(obj.nodeType来判断是否为DOM对象比较勉强,如果普通对象刚好有nodeType属性,那就杯具)
               //window对象
                    if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
                            return false;
                    }
                    try {
                        //自定义的数据类型生成的对象为非普通对象
                        //所谓自定义数据类型即是构造函数的原型属性的值非指向Object.prototype
                            if ( obj.constructor && !hasOwn.call(obj, "constructor") 
                                    && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
                                    return false;
                            }
                    } catch ( e ) {
                        //某些浏览器的内置对象调用hasOwn方法可能抛异常,做非普通对象处理
                            return false;
                    }
                 //for...in...会检索obj的原型链。该语句执行完后,
                 //若key=undefined表示obj为空对象(obj={}或obj=new Object()),则obj必是普通对象;
                 //若key值为仍属于obj的本地属性(ownProperty)而不是obj原型对象的属性,则obj必是普通对象。
                    var key;
                    for ( key in obj ) {}
                    return key === undefined || hasOwn.call( obj, key );
                },
              //object对象遍历执行callback
                each: function( object, callback, args ) {
                    var name, i = 0,
                    //有length属性则将object当做数组处理(Arugments/Array等,String对象也有length属性)
                      length = object.length,
                    //无length属性或为函数类型对象则将object当做对象处理
                      isObj = length === undefined || jQuery.isFunction( object );
                    if ( args ) {
                            if ( isObj ) {
                           //object为对象时则使用for...in...遍历各属性
                                for ( name in object ) {
                                        if ( callback.apply(object[ name ], args)===false ) {
                                            break;
                                        }
                                }
                            } else {
                            //object为数组时则使用for遍历各元素
                                for ( ; i < length; ) {
                                        if ( callback.apply(object[ i++ ], args)===false ) {
                                            break;
                                        }
                                }
                            }
                    } else {
                            if ( isObj ) {
                                for ( name in object ) {
                                 //未传args时,则将object对象的属性及属性值作为参数传入
                                        if ( callback.call(object[name], name, object[ name ])===false ) {
                                            break;
                                        }
                                }
                            } else {
                                for ( ; i < length; ) {
                                  //未传args时,则将object数组的下标及元素值作为参数传入
                                        if ( callback.call(object[ i ], i, object[ i++ ])===false ) {
                                            break;
                                        }
                                }
                            }
                    }
                    return object;
                },
               //去除开始结尾处空格
                //检测浏览器是否已实现trim函数,如是则无需再重新实现
                trim: trim ?
                    function( text ) {
                        return text == null ?"" :trim.call( text );
                    } :
                    function( text ) {
                        return text==null?"":text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
                    },
                makeArray: function( array, results ) {
                    var ret = results || [];
                    if ( array != null ) {
                        var type = jQuery.type( array );
                        if ( array.length == null || type === "string" 
                            || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
                        //非数组Array或非类数组(Arguments)时,则放入数组
                            push.call( ret, array );
                        } else {
                        //数组Array或类数组(Arguments)时,则合并数组
                            jQuery.merge( ret, array );
                        }
                    }
                    return ret;
                },
              //获取数组array元素elem的下标,i为检索的开始下标值,默认为零。
                inArray: function( elem, array, i ) {
                    var len;
                    if ( array ) {
                     //检测indexOf方法浏览器是否已实现,如是则直接调用返回结果
                        if ( indexOf ) {
                            return indexOf.call( array, elem, i );
                        }
                        len = array.length;
                        i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
                        for ( ; i < len; i++ ) {
                            if ( i in array && array[ i ] === elem ) {
                                return i;
                            }
                        }
                    }
                    return -1;
                },
              //合并数据
                merge: function( first, second ) {
                    var i = first.length,
                          j = 0;
                    if ( typeof second.length === "number" ) {
                     //合并数组或类数组的对象
                        for ( var l = second.length; j < l; j++ ) {
                            first[ i++ ] = second[ j ];
                        }
                    } else {
                       //合并属性名为数字的对象
                        while ( second[j] !== undefined ) {
                            first[ i++ ] = second[ j++ ];
                        }
                    }
                    first.length = i;
                    return first;
                },
                //收集通过过滤器函数callback的数组(类数组)元素值
                grep: function( elems, callback, inv ) {
                    var ret = [], retVal;
                     //!!的作用是将inv转化为boolean类型数据,等同于inv=Boolean(inv);
                    //inv=''/0/undefined/null/false,则!!inv=false;inv=其他值,则!!inv=true;               
                    inv = !!inv;
                    for ( var i = 0, length = elems.length; i < length; i++ ) {
                        retVal = !!callback( elems[ i ], i );
                        if ( inv !== retVal ) {
                            ret.push( elems[ i ] );
                        }
                    }
                    return ret;
                },
                //数组(类数组),对象遍历执行callback方法取得新数据后返回
                map: function( elems, callback, arg ) {
                    var value, key, ret = [],
                           i = 0,length = elems.length,
                           //判断elems是否为数组(Array)对象或类数组(如Arguments)对象
                           isArray = elems instanceof jQuery
                        || length !== undefined 
                          && typeof length === "number" 
                                        && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) 
                              || length === 0 || jQuery.isArray( elems ) ) ;
                    if ( isArray ) {
                        for ( ; i < length; i++ ) {
                            value = callback( elems[ i ], i, arg );
                           //若value=null/undefined时value!=null为false
                            if ( value != null ) {
                                //这句技巧性太强,完全可以用ret.push(value)替代
                                ret[ ret.length ] = value;
                            }
                        }
                    } else {
                        for ( key in elems ) {
                            value = callback( elems[ key ], key, arg );
                            //若value=null/undefined时value!=null为false
                            if ( value != null ) {
                                //这句技巧性太强,完全可以用ret.push(value)替代
                                ret[ ret.length ] = value;
                            }
                        }
                    }
                    //concat方法的作用是将数组中内嵌数组元素展开,即是将多维数组转化为一维数组
                    //[].concat.apply([],[1,[2,[3,4]],5,6,7])执行结果为一维数组[1,2,3,4,5,6,7]
                    return ret.concat.apply( [], ret );
                }
           //...... 省略代码
    });
    //初始化class2type变量。该变量存储对象的toString方法返回的字符串(比如[object Object])
    //与该对象类型名称(比如object)的映射关系。供jQuery.type方法使用。
    jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
            class2type[ "[object " + name + "]" ] = name.toLowerCase();
    });
    //...... 省略代码
     
    
     

    总结:作者在判断对象是否是数组或类数组对象时,采用的方式不太一致,这让人很费解。比如方法merge中使用直接typeof second.length === "number" 判断了事,而在map方法中则使用了非常复杂的判断语句:

      elems instanceof jQuery || length !== undefined && typeof length === "number"  
           && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) )
    该判断语句比较严谨。作者应该统一一下的。
  • 相关阅读:
    关于 NSTimer 和 NSRunLoop 的一些理解
    通过 CocoaPods 集成 WeexSDK 到iOS项目中
    iOS 从相册取出的图片默认 取中间部分 裁剪成方形的
    Trilynn分享了炼数成金邀请码
    highcharts分段显示不同颜色
    H5手机开发锁定表头和首列(惯性滚动)解决方案
    为移动端开发提供纯前端的路由方案
    ionic系列
    2014总结
    margin 相关 bug 系列
  • 原文地址:https://www.cnblogs.com/bender/p/3361123.html
Copyright © 2020-2023  润新知