读jQuery源码之三

源码177-527行:jQuery.extend方法

jQuery.extend = jQuery.fn.extend = function() {
    var options, name, src, copy, copyIsArray, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

    // Handle a deep copy situation
    if ( typeof target === "boolean" ) {
        deep = target;

        // Skip the boolean and the target
        target = arguments[ i ] || {};
        i++;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
        target = {};
    }

    // Extend jQuery itself if only one argument is passed
    if ( i === length ) {
        target = this;
        i--;
    }

    for ( ; i < length; i++ ) {
        // Only deal with non-null/undefined values
        if ( (options = arguments[ i ]) != null ) {
            // Extend the base object
            for ( name in options ) {
                src = target[ name ];
                copy = options[ name ];

                // Prevent never-ending loop
                if ( target === copy ) {
                    continue;
                }

                // Recurse if we‘re merging plain objects or arrays
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                    if ( copyIsArray ) {
                        copyIsArray = false;
                        clone = src && jQuery.isArray(src) ? src : [];

                    } else {
                        clone = src && jQuery.isPlainObject(src) ? src : {};
                    }

                    // Never move original objects, clone them
                    target[ name ] = jQuery.extend( deep, clone, copy );

                // Don‘t bring in undefined values
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }

    // Return the modified object
    return target;
};

jQuery.extend({
    // Unique for each copy of jQuery on the page
    expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),

    // Assume jQuery is ready without the ready module
    isReady: true,

    error: function( msg ) {
        throw new Error( msg );
    },

    noop: function() {},

    isFunction: function( obj ) {
        return jQuery.type(obj) === "function";
    },

    isArray: Array.isArray,

    isWindow: function( obj ) {
        return obj != null && obj === obj.window;
    },

    isNumeric: function( obj ) {
        // parseFloat NaNs numeric-cast false positives (null|true|false|"")
        // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
        // subtraction forces infinities to NaN
        // adding 1 corrects loss of precision from parseFloat (#15100)
        return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0;
    },

    isPlainObject: function( obj ) {
        // Not plain objects:
        // - Any object or value whose internal [[Class]] property is not "[object Object]"
        // - DOM nodes
        // - window
        if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
            return false;
        }

        if ( obj.constructor &&
                !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
            return false;
        }

        // If the function hasn‘t returned already, we‘re confident that
        // |obj| is a plain object, created by {} or constructed with new Object
        return true;
    },

    isEmptyObject: function( obj ) {
        var name;
        for ( name in obj ) {
            return false;
        }
        return true;
    },

    type: function( obj ) {
        if ( obj == null ) {
            return obj + "";
        }
        // Support: Android<4.0, iOS<6 (functionish RegExp)
        return typeof obj === "object" || typeof obj === "function" ?
            class2type[ toString.call(obj) ] || "object" :
            typeof obj;
    },

    // Evaluates a script in a global context
    globalEval: function( code ) {
        var script,
            indirect = eval;

        code = jQuery.trim( code );

        if ( code ) {
            // If the code includes a valid, prologue position
            // strict mode pragma, execute code by injecting a
            // script tag into the document.
            if ( code.indexOf("use strict") === 1 ) {
                script = document.createElement("script");
                script.text = code;
                document.head.appendChild( script ).parentNode.removeChild( script );
            } else {
            // Otherwise, avoid the DOM node creation, insertion
            // and removal by using an indirect global eval
                indirect( code );
            }
        }
    },

    // Convert dashed to camelCase; used by the css and data modules
    // Support: IE9-11+
    // Microsoft forgot to hump their vendor prefix (#9572)
    camelCase: function( string ) {
        return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
    },

    nodeName: function( elem, name ) {
        return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
    },

    // 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;
    },

    // Support: Android<4.1
    trim: function( text ) {
        return text == null ?
            "" :
            ( text + "" ).replace( rtrim, "" );
    },

    // 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 {
                push.call( ret, arr );
            }
        }

        return ret;
    },

    inArray: function( elem, arr, i ) {
        return arr == null ? -1 : indexOf.call( arr, elem, i );
    },

    merge: function( first, second ) {
        var len = +second.length,
            j = 0,
            i = first.length;

        for ( ; j < len; j++ ) {
            first[ i++ ] = second[ j ];
        }

        first.length = i;

        return first;
    },

    grep: function( elems, callback, invert ) {
        var callbackInverse,
            matches = [],
            i = 0,
            length = elems.length,
            callbackExpect = !invert;

        // Go through the array, only saving the items
        // that pass the validator function
        for ( ; i < length; i++ ) {
            callbackInverse = !callback( elems[ i ], i );
            if ( callbackInverse !== callbackExpect ) {
                matches.push( elems[ i ] );
            }
        }

        return matches;
    },

    // 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 new values
        if ( isArray ) {
            for ( ; i < length; i++ ) {
                value = callback( elems[ i ], i, arg );

                if ( value != null ) {
                    ret.push( value );
                }
            }

        // Go through every key on the object,
        } else {
            for ( i in elems ) {
                value = callback( elems[ i ], i, arg );

                if ( value != null ) {
                    ret.push( value );
                }
            }
        }

        // Flatten any nested arrays
        return concat.apply( [], ret );
    },

    // A global GUID counter for objects
    guid: 1,

    // Bind a function to a context, optionally partially applying any
    // arguments.
    proxy: function( fn, context ) {
        var tmp, args, proxy;

        if ( typeof context === "string" ) {
            tmp = fn[ context ];
            context = fn;
            fn = tmp;
        }

        // Quick check to determine if target is callable, in the spec
        // this throws a TypeError, but we will just return undefined.
        if ( !jQuery.isFunction( fn ) ) {
            return undefined;
        }

        // Simulated bind
        args = slice.call( arguments, 2 );
        proxy = function() {
            return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
        };

        // Set the guid of unique handler to the same of original handler, so it can be removed
        proxy.guid = fn.guid = fn.guid || jQuery.guid++;

        return proxy;
    },

    now: Date.now,

    // jQuery.support is not used in Core but other projects attach their
    // properties to it so it needs to exist.
    support: support
});

1. jQuery.extend的功能是合并多个对象的属性到第一个对象,第一个参数用来表示是否递归合并,不过我真没看出来有什么场景使用,我试过很多种合并,true和false都是一样的结果,如果你有可以区分的例子,麻烦留言告诉我。

2.如果只传一个参数,则目标对象为jQuery对象,传入的参数会完全合并到jQuery对象里面,相当于给jQuery对象扩展了方法和属性。jQuery里面大部分扩展也是这样做的。比如242行开始的就是一个jQuery内部对extend方法的使用,只传了一个参数(扩展jQuery方法和属性)

3.因为参数个数不限定,所以这里没有设置参数,函数里面用arguments来获取传入的参数。方法里面的细节不用探究,自己实现一个合并两个参数的方法就清楚该咋做了。

4.涉及的几个判断方法

  ①jQuery.isFunction:通过toString.call(this)来判断类型,返回一个去掉了object的类型字符串

  ②jQuery.isArray:直接使用元素的Array.isArray方法判断

  ③jQuery.isPlainObject:判断obj是否为{},new Object()创建

5.isNumeric判断是否为Number:这里用了一个非常巧妙的方式使用(obj - parseFloat( obj ) + 1) >= 0并且排除包含一个数字元素的数组

6.jQuery.each:遍历数组或类数组,对象。

7.guid:初始值为1,用于事件和缓存模块,使用时+1

8.proxy:给函数绑定一个上下文,就是改变this的指向(因为this指的就是当前上下文)

  这个方法里面其实就是把我们传入的对象,使用我们传入的函数.apply,把传入的上下文作为this参数调用该函数。这样就达到了改变函数的this指向的问题。当然完全可以用原生的方法apply或call方法

9.support:用来检测兼容性

接下来的553-2611行都是Sizzle引擎的内容,这部分我也选取一些来分享,因为就个人而言,特别是H5里面新增了document.querySelector和document.querySelectorAll两个方法,支持css选择器,已经满足我们大部分需求。

那继续学习这块代码的目的对我来说就是熟悉里面的实现方式和思想,这个才是重要的。

待续。。。

  

时间: 2024-10-13 21:59:30

读jQuery源码之三的相关文章

读jQuery源码之四(Callbacks,Deferred,when)

看了下Sizzle部分源码,核心的原理就是使用正则表达式去匹配,找到对应的原生获取元素的方法,我没有去细究了.大家有兴趣可以自己看看,分享分享! 从2850行开始,继续往下读jQuery源码(2850-3043行) 进入Callbacks(回调函数管理模块)之前,有几个扩展方法 1.dir方法 三个参数:elem——dom元素,dir——指定elem的层级名称(例如parentNode,nextSibling),until——结束判断.返回一个数组,比如获取某个元素的parentNode,如果不

读jQuery源码 - Deferred

Deferred首次出现在jQuery 1.5中,在jQuery 1.8之后被改写,它的出现抹平了javascript中的大量回调产生的金字塔,提供了异步编程的能力,它主要服役于jQuery.ajax. Deferred就是让一组函数在合适的时机执行,在成功时候执行成功的函数系列,在失败的时候执行失败的函数系列,这就是Deferred的本质.简单的说,模型上可以规划为两个数组来承接不同状态的函数——数组resolve里的函数列表在成功状态下触发,reject中的函数在失败状态下触发. 本文原创于

读jQuery源码之整体框架分析

读一个开源框架,大家最想学到的就是设计的思想和实现的技巧.最近读jQuery源码,记下我对大师作品的理解和心得,跟大家分享,权当抛砖引玉. 先附上jQuery的代码结构. Js代码   (function(){ //jQuery变量定义 var jQuery  = function(){...}; //jQuery原型定义(包含核心方法) jQuery.fn = jQuery.prototype = {...}; //看上去很奇怪吧? 非常巧妙的设计,后面详细介绍 jQuery.fn.init.

时隔多日,终于再次开始读jquery源码

中间有工作忙,也有自己懒惰.大部分是后者. 但是我知道自己真的喜欢js,作为第一个阅读的js开源框架.我一定会坚持下去. ----------------------- 看完了each方法.each会调用传入回调方法.这个用过一次的新手都会很明白. 但是看源码之后自己反倒被自己的弄糊涂了. 原因是测试each时回调方法只写了一句jquery的方法.结果导致each的源码和回调混淆在一起.我还埋怨书中写的不够详细.后来把回调写成了alert(1)终于明朗了. **看来以后一定要注意测试一个框架的源

读jQuery源码之六(Data缓存和Queue队列)

源码3523-3706 Data对象及原型定义 1.accessData方法和Data函数.代码如下: jQuery.acceptData = function( owner ) { // Accepts only: // - Node // - Node.ELEMENT_NODE // - Node.DOCUMENT_NODE // - Object // - Any /* jshint -W018 */ return owner.nodeType === 1 || owner.nodeTyp

读jQuery源码之五(ready事件,access)

源码3373-3461 :主要包含ready事件 // The deferred used on DOM ready var readyList; jQuery.fn.ready = function( fn ) { // Add the callback jQuery.ready.promise().done( fn ); return this; }; jQuery.extend({ // Is the DOM ready to be used? Set to true once it oc

读jQuery源码释疑笔记

1.在jQuery.fn=jQuery.prototype中定义了方法:init, map, each , toArray, get, pushStack,   ready,  slice,first, last, eq, map,end;  属性:jquery, constructor, selector, length, push, sort, splice(后三者仅供内部使用). 2.通过jQuery.fn.init.prototype = jQuery.fn;避免了this的混乱.这样看

读jQuery源码-jQuery的核心理念

jQuery目前最新的版本是3.1.1,未压缩版267KB大小,压缩版86.7KB大小.最新版本不支持IE9以前的浏览器,所以如果要完美兼容IE6,7,8,只能往以前的版本去找了,最好用jQuery1.1.2,因为2.X的版本也不是完整支持IE9以前的浏览器. Write Less,Do More(写更少,做更多),无疑就是jQuery的核心理念,简洁的API.优雅的链式.强大的查询与便捷的操作.

Jquery源码---读《uqery技术内幕,深入解析Jquery架构设计与实现原理》

前两个月项目组特别忙了,买了一本<Juqery技术内幕,深入解析Jquery架构设计与实现原理>一直放着睡大觉:进入八月份项目终于过了TR5点,算是可一个喘口气:这本书终于有时间拜读一下.下面的一两个月我将每天坚持看几页,并陆陆续续写几篇不伦不类的技术博客,谈谈自己的心得体会等等. 首先评价一下这本书吧,我本来想买<锋利的Jquery>,但是电子版翻了一下,感觉还是有点基础了:就在网上找找呀,终于看到了这本---<Juqery技术内幕,深入解析Jquery架构设计与实现原理&