Jquery源码解析(一)

入门前端也一年了,从来没有仔细看过jquery的源码,最近一直在搞angular4,抽点时间写下这个

 (function (global,factory){
     
})(typeof window != 'undefined' ? window : this , function(window,noGlobal){

})

这是jquery的入口,一个自执行函数,相信有基础的都懂。

(function (global,factory){
     "use strict";
     if ( typeof module === "object" && typeof module.exports === "object" ) {
              module.exports = global.document ?
            factory( global, true ) :
            function( w ) {
                if ( !w.document ) {
                    throw new Error( "jQuery requires a window with a document" );
                }
                return factory( w );
            };
    } else {
        factory( global );
    }
})(typeof window != 'undefined' ? window : this , function(window,noGlobal){

})

// For environments that do not have a window with a document
// (such as Node.js), expose a factory as module.exports.
解释一下,如果是node环境,没有window对象和document就用commonjs,利用module.exports导出模块

接下来大家比较关注应该是这个factory工厂函数,我们来看一下里面的内容

function(window,noGlobal){
     var version = "3.2.1",
     // Define a local copy of jQuery
    jQuery = function( selector, context ) {
            return new jQuery.fn.init( selector, context );
    },
}

装个逼,我用的是最新的Jq(3.2.1)
看到了关键字Jquery,这个方法一看就是获取dom节点的,有2个参数:
1.selector:选择器 可以是字符串 正则 等一系列的东东
2.context:上下文 这个选择器是在那个节点下面找到的,默认是body

jQuery = function( selector, context ) {
            return new jQuery.fn.init( selector, context );
    },
var init = jQuery.fn.init = function( selector, context, root ) {
    
};
init.prototype = jQuery.fn;
jQuery.fn = jQuery.prototype = {
}

这段代码如果原型链没有过关的同学看起来比较费劲,简单解释一下吧
init.prototype = jQuery.fn = jQuery.prototype 这个连等都看的懂吧
然后 init = jQuery.fn.init 那么

new jQuery.fn.init() == new init() == new Jquery()
其实这里大费周章 new jQuery.fn.init( selector, context ) 就是返回Jquery一个实例而已

接下来我们比较关心的是 jQuery.fn.init = function( selector, context, root ) {
};里面的代码

var rootjQuery,
    rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
        init = jQuery.fn.init = function( selector, context, root ) {
        var match, elem;
                if ( !selector ) {
            return this;
        }

        root = root || rootjQuery;

        // Handle HTML strings
        if ( typeof selector === "string" ) {
            

        // HANDLE: $(DOMElement)
        } else if ( selector.nodeType ) {
            this[ 0 ] = selector;
            this.length = 1;
            return this;

                // HANDLE: $(function)
          } else if ( jQuery.isFunction( selector ) ) {
            return root.ready !== undefined ?
                root.ready( selector ) :
                               selector( jQuery );
        }
               return jQuery.makeArray( selector, this ); //把获得的dom节点转成数组
    };

可以看得出来源码里面获得节点分3种:
1.selector 是字符串类型的
2.selector 是dom节点类型
3.selector 是函数类型
这里重点讲解一下字符串是如何处理的

var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ),
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;

if ( typeof selector === "string" ) {
            if ( selector[ 0 ] === "<" &&
                selector[ selector.length - 1 ] === ">" &&
                selector.length >= 3 ) {
                        // 以html标签的形式
                match = [ null, selector, null ];
                       } else {
                match = rquickExpr.exec( selector );
            }

            // Match html or make sure no context is specified for #id
            if ( match && ( match[ 1 ] || !context ) ) {

                // HANDLE: $(html) -> $(array)
                if ( match[ 1 ] ) {
                    context = context instanceof jQuery ? context[ 0 ] : context;

                    // Option to run scripts is true for back-compat
                    // Intentionally let the error be thrown if parseHTML is not present
                    jQuery.merge( this, jQuery.parseHTML(
                        match[ 1 ],
                        context && context.nodeType ? context.ownerDocument || context : document,
                        true
                    ) );

                    return this;

                // HANDLE: $(#id)  处理id选择器
                } else {
                    elem = document.getElementById( match[ 2 ] );

                    if ( elem ) {

                        // Inject the element directly into the jQuery object
                        this[ 0 ] = elem;
                        this.length = 1;
                    }
                    return this;
                }

            // HANDLE: $(expr, $(...))  利用内部的find方法寻找节点
            } else if ( !context || context.jquery ) {
                return ( context || root ).find( selector );

            // (which is just equivalent to: $(context).find(expr)
            } else {
                return this.constructor( context ).find( selector );
            }

    
        } 

这一段看起来确实费劲,关于js里面强大的exec正则用法,大家自己可以研究
context = context instanceof jQuery ? context[ 0 ] : context; 判断context是否是jquery实例

下面来看jquery的原型里面有哪些代码

var version = "3.2.1",
   jQuery = function( selector, context ) {
            return new jQuery.fn.init( selector, context );
        };
   jQuery.fn = jQuery.prototype = {
        jquery: version,
        constructor: jQuery,
        length: 0,
         toArray: function() {
            return slice.call( this );
        },
         get: function( num ){
          if ( num == null ) {
                return slice.call( this );
            }

            return num < 0 ? this[ num + this.length ] : this[ num ];
        },
          pushStack: function( elems ) {
            var ret = jQuery.merge( this.constructor(), elems );
           ret.prevObject = this;
           return ret;
        },

       each: function( callback ) {
            return jQuery.each( this, callback );
        },

        map: function( callback ) {
            return this.pushStack( jQuery.map( this, function( elem, i ) {
                return callback.call( elem, i, elem );
            } ) );
        },

        slice: function() {
            return this.pushStack( slice.apply( this, arguments ) );
        },

        first: function() {
            return this.eq( 0 );
        },

        last: function() {
            return this.eq( -1 );
        },

        eq: function( i ) {
            var len = this.length,
                j = +i + ( i < 0 ? len : 0 );
            return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
        },

        end: function() {
            return this.prevObject || this.constructor();
        },
        push: push,
        sort: arr.sort,
        splice: arr.splice
        // 这里的push  sort  splice就是数组的方法
    };

看到了吧 这里涵盖了所有jquery对象的方法
1.我们要把一个jq Dom对象转成数组,方便遍历,可以 $('').toArray()
2.获取节点中某一个可以用 $('****').get(index)
3.注意pushStack是一个很重要的函数,slice函数就用到了他
4.可以通过$('
').slice(index) 截取部分dom元素
5.each map等函数在这里也能看到。

难道强大的Jquery原型上就这些方法,当然不是,我们接着看

jQuery.extend = jQuery.fn.extend = function() {
        var options, name, src, copy, copyIsArray, clone,
            target = arguments[ 0 ] || {},
            i = 1,
            length = arguments.length,
            deep = false;
        //大家记得这个函数吗 $.extend(true,obj1,obj2,obj3)  第一个参数为true表示深克隆
        if ( typeof target === "boolean" ) {
            deep = target;
            target = arguments[ i ] || {};
            i++;
        }

        if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
            target = {};
        }
        // 当没有传递bool值时  参数只有一个时  也就是$.fn.extend({})
        if ( i === length ) {
            target = this;
            i--;
        }

        for ( ; i < length; i++ ) {
            if ( ( options = arguments[ i ] ) != null ) {
                for ( name in options ) {
                    src = target[ name ];
                    copy = options[ name ];
                    if ( target === copy ) {
                        continue;
                    }
                    // 这就是深克隆的情况
                    if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
                        ( copyIsArray = Array.isArray( copy ) ) ) ) {

                        if ( copyIsArray ) {
                            copyIsArray = false;
                            clone = src && Array.isArray( src ) ? src : [];

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

                        target[ name ] = jQuery.extend( deep, clone, copy );
                     // 这种情况就实现了jquery原型方法的拓展 
                    } else if ( copy !== undefined ) {
                        target[ name ] = copy;
                    }
                }
            }
        }
        return target;
    };

有了以上代码,我们这样玩一哈

$.fn.extend({
     say:function(){
        console.log(this.nodeName)
},
    hasChild:function(ele){
        return $('ele this').length > 0
}
})

上面2个方法适用于任何jq对象,每个jq对象都有say方法,打印自己的nodeType,还有个hasChild方法判断是否有某个子节点。
那我们来看看jquery源码里面对原型上作了哪些扩展

jQuery.fn.extend( {
    find: function( selector ) {
        var i, ret,
            len = this.length,
            self = this;

        if ( typeof selector !== "string" ) {
            return this.pushStack( jQuery( selector ).filter( function() {
                for ( i = 0; i < len; i++ ) {
                    if ( jQuery.contains( self[ i ], this ) ) {
                        return true;
                    }
                }
            } ) );
        }

        ret = this.pushStack( [] );

        for ( i = 0; i < len; i++ ) {
            jQuery.find( selector, self[ i ], ret );
        }

        return len > 1 ? jQuery.uniqueSort( ret ) : ret;
    },
    filter: function( selector ) {
        return this.pushStack( winnow( this, selector || [], false ) );
    },
    not: function( selector ) {
        return this.pushStack( winnow( this, selector || [], true ) );
    },
    is: function( selector ) {
        return !!winnow(
            this,
                typeof selector === "string" && rneedsContext.test( selector ) ?
                jQuery( selector ) :
                selector || [],
            false
        ).length;
    },
     addBack: function( selector ) {
        return this.add( selector == null ?
            this.prevObject : this.prevObject.filter( selector )
        );
    }
} );

是不是看到了很多熟悉的jq方法,然而这些方法是被扩展进去的
注意find()方法是递归查找,会一直找下去,效率并不高。
addBack()方法的源码就在这

<ul>
  <li>list item 1</li>
  <li>list item 2</li>
  <li class="third-item">list item 3</li>
  <li>list item 4</li>
  <li>list item 5</li>
</ul>
$( "li.third-item" ).nextAll().addBack()
  .css( "background-color", "red" );

这里就不再赘述了,大家可以到源码中自行查找。
下面说一个经常见得工具方法 $.extend()

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

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

    isNumeric: function( obj ) {

        // As of jQuery 3.0, isNumeric is limited to
        // strings and numbers (primitives or objects)
        // that can be coerced to finite numbers (gh-2662)
        var type = jQuery.type( obj );
        return ( type === "number" || type === "string" ) &&

            // parseFloat NaNs numeric-cast false positives ("")
            // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
            // subtraction forces infinities to NaN
            !isNaN( obj - parseFloat( obj ) );
    },

    isPlainObject: function( obj ) {
        var proto, Ctor;

        // Detect obvious negatives
        // Use toString instead of jQuery.type to catch host objects
        if ( !obj || toString.call( obj ) !== "[object Object]" ) {
            return false;
        }

        proto = getProto( obj );

        // Objects with no prototype (e.g., `Object.create( null )`) are plain
        if ( !proto ) {
            return true;
        }

        // Objects with prototype are plain iff they were constructed by a global Object function
        Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
        return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
    },
       each: function( obj, callback ) {
        var length, i = 0;

        if ( isArrayLike( obj ) ) {
            length = obj.length;
            for ( ; i < length; i++ ) {
                if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
                    break;
                }
            }
        } else {
            for ( i in obj ) {
                if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
                    break;
                }
            }
        }

        return obj;
    }

我们在日常开发中是不是会常用到上述工具函数,判断对象类型等
最常用的还是$.each(obj,function()) 这个方法了吧
也可以自定义一个工具函数来供自己使用

$.extend({
    zhishu:function(num){
      var count = 0,flag = true;
      while(num--){
        for(var i =0; i<Math.sqrt(num);i++){
            if(num%i == 0) {
               flag = !flag
               break
        }
           count++    
       }
   }
}
})

以上就写了方法查找某个数范围内的质数
有一点也许大家还在像jquery和$是怎么联系到一起的。。。

//当有一个也是以$开头的库与jquery冲突时 
var _jQuery = window.jQuery,
       // Map over the $ in case of overwrite
    _$ = window.$;

//这是获取之前定义的原始$
jQuery.noConflict = function( deep ) {
       //如果现在定义的Jquery与之前不一样,那么之前的就得写成_$  _JQuery
    if ( window.$ === jQuery ) {
        window.$ = _$;
    }

    if ( deep && window.jQuery === jQuery ) {
        window.jQuery = _jQuery;
    }

    return jQuery;
};
if ( !noGlobal ) {
    window.jQuery = window.$ = jQuery;
}
// 在非全局情况下 Jquery  $ 都有jq原型

举个栗子

<html lang="en">
<head>
  <meta charset="utf-8">
  <title>jQuery.noConflict demo</title>
  <script src="https://code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>
 
<div id="log">
  <h3>Before $.noConflict(true)</h3>
</div>
<script src="https://code.jquery.com/jquery-1.6.2.js"></script>
 
<script>
var $log = $( "#log" );
 
$log.append( "2nd loaded jQuery version ($): " + $.fn.jquery + "<br>" );
 
// Restore globally scoped jQuery variables to the first version loaded
// (the newer version)
 
jq162 = jQuery.noConflict( true );
 
$log.append( "<h3>After $.noConflict(true)</h3>" );
$log.append( "1st loaded jQuery version ($): " + $.fn.jquery + "<br>" );
$log.append( "2nd loaded jQuery version (jq162): " + jq162.fn.jquery + "<br>" );
</script>
 
</body>
</html>
Before $.noConflict(true)

2nd loaded jQuery version ($): 1.6.2
After $.noConflict(true)

1st loaded jQuery version ($): 1.10.2
2nd loaded jQuery version (jq162): 1.6.2

今天的内容就到这里,下回我们讲解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 1.jQuery是如何寻找到DOM或者很多方法 $符号选择标签,然后进行事件监听或者DOM操作 可以把$,看成一个...
    大淀桑浮不起來阅读 904评论 1 10
  • 今天,更新有点晚, 但是干货不怕晚, 接着昨天讲得讲, 把第一段讲的没讲完的继续讲解。 (function(){ ...
    web_无笙阅读 367评论 0 2
  • 继续咱们的jQuery源码解析。 (function(){ 原文中此处为链接,暂不支持采集 (96,2...
    web_无笙阅读 398评论 2 1
  • 1.JQuery 基础 改变web开发人员创造搞交互性界面的方式。设计者无需花费时间纠缠JS复杂的高级特性。 1....
    LaBaby_阅读 1,101评论 0 1
  • 我曾经是有个深爱的他的,是的,只是曾经…我是个从小缺爱的人,他出门了,别人以为他去买东西,我却以为他要把...
    乏味君阅读 284评论 0 1