jQuery源码学习笔记(2) – init初始化

代码:jquery-1.4.2.js

对jQuery印象最深的东西,莫过于他的“$”符合了,它和jQuery关键字是等价的,我们可以用它来做哪些事呢?

先看看官方的API:

1. jQuery(selector, [context])

Accepts a string containing a CSS selector which is then used to match a set of elements.

接受一个CSS选择器的字符串,用于获取文档中的节点元素。

2. jQuery(html, [ownerDocument])

Creates DOM elements on the fly from the provided string of raw HTML.

根据提供的HTML字符串创建DOM元素。

3. jQuery(callback)

Binds a function to be executed when the DOM has finished loading.

定义DOMContentLoaded事件完成时的回调函数。

 

下面我们进入源码来看看。

jQuery源码的前几行是这样的:

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

定义了一个jQuery的方法,这个方法实际调用的是jQuery.fn.init,创建一个新的jQuery对象,其实这个jQuery.fn.init就是jQuery的入口,实现了上面提到的所有方法

jQuery.fn.init定义在源码的第75行:

jQuery.fn = jQuery.prototype = {
	init: function( selector, context ) {
	//很长的一段代码
	}
	//其他原型方法
}

init方法是定义在jQuery对象的原型上的,所有对象共用此方法(不知算不算设计模式中的原型模式,有点不同,js是诡异的)

 

根据官方文档的说明,我将init函数的功能分为了3类,每类又根据代码的逻辑分为几个小方法

一、jQuery( selector, [context] )

//获取DOM元素

  • 1、jQuery( selector, [context])
    • 1.1、body 唯一的body标签,直接返回
    • 1.2、#id 调用getElementById
    • 1.3、tagName 调用getElementsByTagName
    • 1.4、css选择器, [context] 调用jQuery.find
  • 2、jQuery( element ) 封装,直接返回
  • 3、jQuery( elementArray )
  • 4、jQuery( jQuery object )
  • 5、jQuery()

二、jQuery( html, [ownerDocument] )

//动态创建由 jQuery 对象包装的 DOM 元素

  • 6、jQuery( html, [ownerDocument] )
    • 6.1、SingleTag 单个标签 createNode
    • 6.2、HTML Fragment HTML代码段
  • 7、jQuery( html, props ) 创建的DOM,并添加属性props

三、jQuery( callback )

//等价于$(document).ready(callback)

  • 8、jQuery( callback )

 

为了说明init函数的运行机制,我就结合代码来讲了,保留了原注释,可以比较着看。

jQuery.fn = jQuery.prototype = {
  init: function( selector, context ) {
    //match 保存正则表达式的匹配结果
    //doc 当前文档对象document
    //ret 正则表达式匹配结果
    //elem getElementById返回结果
    //下面只是定义一些会用到的局部变量, 不用管
    var match, elem, ret, doc;

    //Handle $(""), $(null), or $(undefined)
    //不提供任何参数,则返回一个空jQuery对象。
    //也就是方法5: jQuery()
    if ( !selector ) {
      return this;
    }

    //Handle $(DOMElement)
    //如果nodeType属性存在,说明是一个DOM节点对象
    //直接进行封装,返回
    //方法2: jQuery( element )
    if ( selector.nodeType ) {
      this.context = this[0] = selector;
      this.length = 1;
      return this;
    }

    //The body element only exists once,
	//optimize finding it
    //如果传入的是字符串"body"且context为空,
	//则将document.body作为选择结果
    //因为body节点只有一个,就不用去找了(属于方法1)
    if ( selector === "body" && !context ) {
      this.context = document;
      this[0] = document.body;
      this.selector = "body";
      this.length = 1;
      return this;
    }

    //Handle HTML strings
    //如果是字符串
    if ( typeof selector === "string" ) {
      //Are we dealing with HTML string or an ID?
      //根据正则表达式判断是否在处理html字符串或者节点ID
      //quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/
      //quickExpr的\1为html标签名,\2为ID
      //注:\1 2 ... n表示正则表达式中()的匹配值
      match = quickExpr.exec( selector );

      //Verify a match, and that no context
	  //was specified for #id
      //如果有匹配,且是一个html字符串,或者没有第二个参数
      if ( match && (match[1] || !context) ) {

        //HANDLE: $(html) -> $(array)
        //如果匹配\1, 即为HTML字符串(第二类方法)
        if ( match[1] ) {
          //doc为当前处理的文档对象
          //如果有第二个参数,
          //且它的ownerDocument存在,
          //就返回它的ownerDocument(该元素所在的文档)
          //如果没有第二个参数,则返回window.document
          doc = (context ? context.ownerDocument ||
			  context : document);

          //If a single string is passed in
          //and it's a single tag
          //just do a createElement and skip the rest
          //如果是一个没有文本的标签,直接创建DOM元素
          //rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/
          ret = rsingleTag.exec( selector );

          //如果匹配rsingleTag成功
          //selector是一个没有文本的标签
          if ( ret ) {
            if ( jQuery.isPlainObject( context ) ) {
               //如果第二个参数context是一个普通对象,则为
               //方法7: jQuery( html, props )
               //props是用于附加到新创建元素上的属性、事件和方法

              //将创建的元素放入选择器
              selector = [document.createElement(ret[1])];
              //TODO: 将参数赋值给创建的元素
              jQuery.fn.attr.call(selector, context, true);

            } else {
              //如果第二个参数不是普通对象,
              //直接将创建的元素放入选择器
              //此时用的document是之前
              //根据context获得的ownerDocument
              //即为方法6: jQuery(html, [ownerDocument])
              selector = [ doc.createElement( ret[1] ) ];
            }

          } else {
            //如果没有匹配成功, 说明是一个html文档片段
            //创建多标签元素
            ret = buildFragment( [ match[1] ], [ doc ] );
            selector = (ret.cacheable ?
				ret.fragment.cloneNode(true) :
				ret.fragment).childNodes;
          }
          //将selector中的元素合并到jQuery中,然后返回
          //此处返回的内容包括方法6、方法7(第二类方法)
          return jQuery.merge( this, selector );

        //HANDLE: $("#id")
        } else {
          //没有匹配\1,说明是一个元素的ID
          elem = document.getElementById( match[2] );

          if ( elem ) {
            //Handle the case where
            //IE and Opera return items
            //by name instead of ID
            //处理IE和Opera会用name代替ID返回元素的问题
            //(应该是个古老的问题,我测试到好像不会)
            if ( elem.id !== match[2] ) {
              //TODO: find方法
              return rootjQuery.find( selector );
            }

            //Otherwise, we inject the element
            //directly into the jQuery object
            //找到了就直接放进jQuery对象中
            this.length = 1;
            this[0] = elem;
          }

          this.context = document;
          this.selector = selector;
          //返回id匹配的元素
          return this;
        }

      //下面是没有匹配quickExpr,
      //或者不是html又有第二个参数的情况
      //HANDLE: $("TAG")
      } else if ( !context && /^\w+$/.test( selector ) ) {
        //没有第二个参数,且第一个参数selector就是单词字符串
        //也就是tagName的情况
        this.selector = selector;
        this.context = document;
        selector = document.getElementsByTagName(selector);
        //将获取的nodeList放入jQuery对象中,返回
        return jQuery.merge( this, selector );

      //HANDLE: $(expr, $(...))
      //有第二个参数,第一个参数是字符串,
	  //但不是html也不是id也不是tagName
      //也就是CSS选择器了(如 .menu a 之类的)
      //下面是方法1: jQuery( selector, [context])
      } else if ( !context || context.jquery ) {
        //调用jQuery对象的find方法
        //find方法就是针对复杂表达式的
        //没有第二个参数则使用rootjQuery
        //或通过第二个参数(一个jQuery对象)调用
        return (context || rootjQuery).find( selector );

      //HANDLE: $(expr, context)
      //(which is just equivalent to: $(context).find(expr)
      } else {
        //第二个参数不是jQuery对象,封装,再调用find方法
        return jQuery( context ).find( selector );
      }

    //HANDLE: $(function)
    //Shortcut for document ready
    //如果第一个参数是一个方法,
    //则当做document ready事件的回调函数
    //即方法8: jQuery( callback )
    } else if ( jQuery.isFunction( selector ) ) {
      return rootjQuery.ready( selector );
    }

    if (selector.selector !== undefined) {
      //传入的是一个jQuery对象
      //也就是方法4: jQuery( jQuery object )
      this.selector = selector.selector;
      this.context = selector.context;
    }
    //就剩下方法3和方法4没返回,这里一起解决它们~
    //将selector中的元素放入this中
    //makeArray底层是调用merge方法,但是排除了一些特殊情况
    return jQuery.makeArray( selector, this );
  }

  /* other prototype function */
}

 

上面这种风格做源码解读,不知道好不好呢?欢迎各种意见或建议~

 

我说:好长一坨代码,看的我都晕了 [晕] ~

发布者

Rolf

伪文艺IT攻城师,热爱前端,热爱互联。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

[喜欢] [嘻嘻] [奋斗] [问号] [鼓掌] [泪] [酷] [强] [耶] [握手] [心] [给力] [神马] [围观] [奥特曼] more »