jquery 源码分析一

(function( global, factory ) {

    if ( typeof module === "object" && typeof module.exports === "object" ) {
        // For CommonJS and CommonJS-like environments where a proper window is present,
        // execute the factory and get jQuery
        // For environments that do not inherently posses a window with a document
        // (such as Node.js), expose a jQuery-making factory as module.exports
        // This accentuates the need for the creation of a real window
        // e.g. var jQuery = require("jquery")(window);
        // See ticket #14549 for more info
        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 );
    }

// Pass this if window is not defined yet
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
    //jQuery code goes here
}));

这是1.11.0版jquery开头的一段源码,这段源码的意义是为了能够兼容一些新的js用处,如nodeJS,在上面的英文注释中已经写得很详细了,就不多做解释了。其最终还是想后面的函数传入了一个window或者类window对象和一个布尔值。

jQuery的实例化主要由以下这段函数实现

jQuery = function( selector, context ) {
    // The jQuery object is actually just the init constructor 'enhanced'
    // Need init if jQuery is called (just allow error to be thrown if not included)
    return new jQuery.fn.init( selector, context );
}

这段函数主要是通过实例化jQuery.fn.init来实现的,一模一样的参数传入。不过在分析init内容之前,有一个问题,那就是如果这样实例化的话,原先加载在jQuery.prototype上的方法就应该不能被使用,可是最后还是可以调用,这是为什么呢。。原因就是有这么一句代码:

jQuery.fn = jQuery.prototype = {
    //some codes
}
//some codes
init.prototype = jQuery.fn;

这样使得jQuery.fn,jQuery.prototype和init.prototype引用的是同一个对象,由于对象是引用型变量,所以在这句话以后对jQuery.prototype的修改,还是会在jQuery.fn上体现出来,也会在init.prototype上出现。这样实例化以后就会得到所有jQuery方法。

接下来就是init方面的分析了,分析如下:

  1 init = jQuery.fn.init = function( selector, context ) {
  2     var match, elem;
  3 
  4     // 处理那些为空选择的情况,$(),$(false),$(null)等
  5     if ( !selector ) {
  6         return this;
  7     }
  8 
  9     // 处理HTML字符串,如<div></div>
 10     if ( typeof selector === "string" ) {
 11         if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
 12             // 认为标签从<开始>结束,且长度大于3
 13             match = [ null, selector, null ];
 14 
 15         } else {
 16             match = rquickExpr.exec( selector );      //rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
 17         }
 18 
 19         // 确认是HTML字符串而不是#id
 20         if ( match && (match[1] || !context) ) {
 21 
 22             // 将HTML字符串转化为合适的DOM
 23             if ( match[1] ) {
 24                 context = context instanceof jQuery ? context[0] : context;
 25 
 26                 // 得到上下文环境context
 27                 // 用parseHTML将字符串转化为DOM,然后将之合并到this中,merge适合于数组的合并
 28                 jQuery.merge( this, jQuery.parseHTML(
 29                     match[1],
 30                     context && context.nodeType ? context.ownerDocument || context : document,
 31                     true
 32                 ) );
 33 
 34                 // 处理$(html,props),这段函数将对上面生成的DOM加入prop中的属性,如class,style。rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/)
 35                 if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
 36                     for ( match in context ) {
 37                         // 若是事件函数,则调用this中相应的添加事件函数的方法,将函数绑定
 38                         if ( jQuery.isFunction( this[ match ] ) ) {
 39                             this[ match ]( context[ match ] );
 40 
 41                         // 设置属性值
 42                         } else {
 43                             this.attr( match, context[ match ] );
 44                         }
 45                     }
 46                 }
 47 
 48                 return this;
 49 
 50             // 处理$(#id)的情况
 51             } else {
 52                 elem = document.getElementById( match[2] );
 53 
 54                 // 判断是否有parentnode
 55                 // 因为在某些浏览器下getElementById会返回已删除的节点
 56                 if ( elem && elem.parentNode ) {
 57                     // IE和Opera下面的特殊情况
 58                     // 会返回name的情况
 59                     if ( elem.id !== match[2] ) {
 60                         return rootjQuery.find( selector );
 61                     }
 62 
 63                     // 正常的浏览器下就直接放入到this里就好了
 64                     this.length = 1;
 65                     this[0] = elem;
 66                 }
 67 
 68                 this.context = document;
 69                 this.selector = selector;
 70                 return this;
 71             }
 72 
 73         // 处理$(expr)或者$(expr,$(..)),其中rootjQuery返回的是$(document)
 74         } else if ( !context || context.jquery ) {
 75             return ( context || rootjQuery ).find( selector );
 76 
 77         // 处理$(expr,context),注意这里的context并不是jQuery对象
 78         // 其实这段函数的原理就是jQuery(context).find(selector),因为this.constructor指向的就是jQuery
 79         } else {
 80             return this.constructor( context ).find( selector );
 81         }
 82 
 83     // HANDLE: $(DOMElement)就是在这里处理的
 84     } else if ( selector.nodeType ) {
 85         this.context = this[0] = selector;
 86         this.length = 1;
 87         return this;
 88 
 89     // 处理$(function)
 90     // 即在document ready时调用
 91     } else if ( jQuery.isFunction( selector ) ) {
 92         return typeof rootjQuery.ready !== "undefined" ?
 93             rootjQuery.ready( selector ) :
 94             // 如果没有ready的话,就立刻调用
 95             selector( jQuery );
 96     }
 97 
 98     if ( selector.selector !== undefined ) {
 99         this.selector = selector.selector;
100         this.context = selector.context;
101     }
102 
103     return jQuery.makeArray( selector, this );
104 }

 要注意的几点:

在这段函数中间设置断点时,让其返回this,它会返回[]。然而,正常的理解应该返回的是一个对象,而不是数组。这是因为在prototype的属性中设置了一下几个值:

length: 0,
push:[].push,
sort:[].sort,
splice:[].splice

这样看起来就像数组了,于是调试返回的时候会出现[]........

先就这些,以后想到啥了再补。。。。。

posted @ 2014-03-01 22:44  胖蝎子  阅读(779)  评论(0编辑  收藏  举报