jQuery源码分析之init方法

jQuery函数源码如下:

[javascript]  view plain  copy
  1. jQuery = function( selector, context ) {  
  2.     // The jQuery object is actually just the init constructor 'enhanced'  
  3.     // Need init if jQuery is called (just allow error to be thrown if not included)  
  4.     return new jQuery.fn.init( selector, context );  
  5. }  
注意:按照这种写法最后返回的应该是init对象类型,为什么结果是jQuery类型呢,这里就用到了原型链的知识,init.prototype = jQuery.fn;所以把jQuery的原型设置为init的原型。
同时源码中使用对象字面量的形式对jQuery的原型赋值的,那么jQuery函数的constructor属性就会被覆盖,所以要手动修正。如下例:

[javascript]  view plain  copy
  1. 因为constructor属性很容易被修改,constructor: jQuery所以会出现在源码中  
  2.     function A(){}  
  3.     //第二种方式直接把A的constructor属性覆盖掉了,所以这时候的new A().constructor就是function object(){[native code]}  
  4.     //因为jQuery源码中就是第二种方式,所以要对constructor进行重新声明!  
  5.     A.prototype={  
  6.       name:"xxx",  
  7.       sex:"female",  
  8.       constructor:A  
  9.     }  
jQuery中init函数源码分析:

[javascript]  view plain  copy
  1.  init = jQuery.fn.init = function( selector, context ) {  
  2.     var match, elem;  
  3.     // HANDLE: $(""), $(null), $(undefined), $(false)  
  4.     //直接return this这个对象就是空的init对象!  
  5.     if ( !selector ) {  
  6.         return this;  
  7.     }  
  8.     // Handle HTML strings  
  9.     if ( typeof selector === "string" ) {  
  10.     //id选择器,class选择器等都是走else  
  11.         if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {  
  12.             // Assume that strings that start and end with <> are HTML and skip the regex check  
  13.             match = [ null, selector, null ];  
  14.         } else {  
  15.         //$("<li>hello").appendTo("ul")相当于只是添加空标签<li></li>和$("<li/>").appendTo("ul")一样!  
  16.         //var rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/  
  17.         match = rquickExpr.exec( selector );  
  18.         //对于$("#div1")和$("<div>hello")  
  19.         //matched=null//$(".box"),$("div"),$("#div1 div.box")  
  20.         //matched=["#div1,null,'div1'"]//对于$("#div1")  
  21.         //matched=["<li>hello",'<li>',null]//对于$("<li>hello")  
  22.         }  
  23.         //创建标签如:$("<li></li>")那么就会走这里的逻辑!或者$("<li>hello")也会走这里的逻辑!  
  24.         //id选择器也会走这里的逻辑,虽然matched[1]是false,但是context是不存在的,因为我们的id选择器一般不传入上下文!  
  25.         //如$("#div1"),因为id是唯一的!  
  26.         // Match html or make sure no context is specified for #id  
  27.         if ( match && (match[1] || !context) ) {  
  28.             // HANDLE: $(html) -> $(array)  
  29.             if ( match[1] ) {  
  30.     //创建标签怎么会有上下文呢?只能是$("<li>",document)第二个参数只能是document,如果是iframe,那么是contentWindow.document!  
  31.             //默认情况是document!  
  32.             //如果context直接是document那么直接返回document,如果context是$(document)那么就获取到原生的document!  
  33.                 context = context instanceof jQuery ? context[0] : context;  
  34.                 //$("<li>1</li><li>2</li>")如何把字符串转化为this={0:'li',1:'li',length:2}  
  35.                 //因为后面的是DOM节点数组!于是通过parseHTML来完成!parseHTML第三个参数默认是false  
  36.     //但是parseHTML也可以创建script标签(注意写script标签的时候要转义,防止他和上面的script配对!)true表示可以添加script标签!  
  37.                 // scripts is true for back-compat  
  38.                 // Intentionally let the error be thrown if parseHTML is not present  
  39.     //怎么把后面的DOM数组转化为上面的JSON格式呢?用merge方法!merge方法也可以对JSON进行合并!下标必须是数字,length也是数字!  
  40.                 //这里的this就是new init函数的一个空对象!  
  41.                 jQuery.merge( this, jQuery.parseHTML(  
  42.                     match[1],  
  43.                     context && context.nodeType ? context.ownerDocument || context : document,  
  44.                     true  
  45.                 ) );  
  46.                 // HANDLE: $(html, props)  
  47.                 //var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);  
  48.                 //$("<li>",{tile:"abcd",html:'abcn'})  
  49.                 //这个正则是匹配单标签,如<li> 或者<li></li>也就是对$("<li></li><li></li>",{})不行!  
  50.                 //isPlainObject用于判断是否为对象字面量!  
  51.                 if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {  
  52.                     for ( match in context ) {  
  53.                         // Properties of context are called as methods if possible  
  54.                         //因为上面的html方法已经存在,于是直接调用html,如直接调用this.html("abcn")  
  55.                         //其它同名的在jQuery中源码方法如css也是直接调用!  
  56.                         if ( jQuery.isFunction( this[ match ] ) ) {  
  57.                             this[ match ]( context[ match ] );  
  58.                         // ...and otherwise set as attributes  
  59.                         } else {  
  60.                             this.attr( match, context[ match ] );  
  61.                         }  
  62.                     }  
  63.                 }  
  64.                 return this;  
  65.               //因为页面中id是唯一的,所以不需要上下文!  
  66.            ////matched=["#div1,null,'div1'"]//对于$("#div1")  
  67.             // HANDLE: $(#id)  
  68.             } else {  
  69.                 elem = document.getElementById( match[2] );  
  70.                     //也就是在黑莓4.6中页面中没有,但是克隆的时候还是能够找到,所以添加一个  
  71.                  //是否有parentNode,如果一个元素不存在那么父元素肯定不存在!  
  72.                 // Check parentNode to catch when Blackberry 4.6 returns  
  73.                 // nodes that are no longer in the document #6963  
  74.                 if ( elem && elem.parentNode ) {  
  75.                     // Handle the case where IE and Opera return items  
  76.                     // by name instead of ID\  
  77.                     //在IE或者Opera浏览器中,返回的是用name查询的结果,而不是id!  
  78.                     //那么调用:rootjQuery = jQuery( document );  
  79.                     if ( elem.id !== match[2] ) {  
  80.                         return rootjQuery.find( selector );  
  81.                     }  
  82.                         //Jquery创建的时候是JSON类型不是数组,所以length要手动赋值!  
  83.                     //可以参见上面有constructor,length属性等!  
  84.                     // Otherwise, we inject the element directly into the jQuery object  
  85.                     this.length = 1;  
  86.                     this[0] = elem;  
  87.                 }  
  88.                 //id选择器的上下文肯定是document!  
  89.                 this.context = document;  
  90.                 //每一个jQuery对象的都有一个selector属性,表示$("#test")存储其中的"#test"字符串!  
  91.                 this.selector = selector;  
  92.                 return this;  
  93.             }  
  94.             //上面的id的情况和创建元素的情况已经处理完毕了!  
  95.         // HANDLE: $(expr, $(...))  
  96.         //如果没有传入上下文或者传入的上下文是jQuery对象!那么调用上下文的find方法继续缩小查找范围!  
  97.         //如$("li",$("ul"))  
  98.         } else if ( !context || context.jquery ) {  
  99.             return ( context || rootjQuery ).find( selector );  
  100.         // HANDLE: $(expr, context)  
  101.         // (which is just equivalent to: $(context).find(expr)  
  102.         //如果上下文不是jQuery对象,那么直接把上下文封装为jQuery对象继续调用find方法  
  103.         //this.constructor=jQuery  
  104.         } else {  
  105.             return this.constructor( context ).find( selector );  
  106.         }  
  107.   
  108.     // HANDLE: $(DOMElement)  
  109.     //调用方式:$(this),$(document)  
  110.     //直接把它封装为jQuery对象,这里是selector不是string的情况!  
  111.     } else if ( selector.nodeType ) {  
  112.     //保存到JSON的第0个元素上面!  
  113.         this.context = this[0] = selector;  
  114.         this.length = 1;  
  115.         return this;  
  116.     // HANDLE: $(function)  
  117.     // Shortcut for document ready  
  118.     //rootjQuery = jQuery( document );  
  119.     //文档加载两种方式:$(function(){})是简写的方式,最终调用的还是$(document).ready(function(){})  
  120.     //如果selecot是function,那么调用调用$(document).ready(function(){}),如果传入的是函数但是$(document).ready是undfined那么  
  121.     //直接把jQuery对象传入这个参数进行调用!  
  122.     } else if ( jQuery.isFunction( selector ) ) {  
  123.         return typeof rootjQuery.ready !== "undefined" ?  
  124.             rootjQuery.ready( selector )   
  125.             // Execute immediately if ready is not present  
  126.             selector( jQuery );  
  127.     }  
  128.       //如果调用方式是:$($("#div1"))那么要包装成为$("#div1")  
  129.     //jQuery对象有selector属性!DOM对象有nodeType属性!  
  130.     if ( selector.selector !== undefined ) {  
  131.         this.selector = selector.selector;  
  132.         this.context = selector.context;  
  133.     }  
  134.          //因为makeArray在内部调用的时候如果第二个参数传入的是JSON对象,那么最后返回的结果就是JSON!  
  135.     return jQuery.makeArray( selector, this );  
  136. };  
总结:

一,首先判断传入的第一个参数selector是否为string类型,如果不是string类型那么走下面的“二,三,四”,否则就是走"1,2,3"

  1,判断是否是创建HTML标签或者id选择器,如果是创建HTML标签那么调用parseHTML方法得到DOM数组,并且把这个数组封装到JSON对象this上,同时,创建的时候第二个参数可以是一个对象,这时候如果对象上有jQuery的同名方法那么直接调用!(merge第一个参数为JSON情况),如果是id选择器那么调用getElementById获取对象,然后封装到this上面!

 2,判断第二个参数context如果不存在或者存在同时是对象,那么把这个对象封装成为jQuery对象,同时调用该对象的find方法!(对于class等选择器都是最终调用find方法)

二,如果selector是DOM,那么直接把DOM封装到this[0]上面,并返回这个jQuery对象!(源码中的this一直就是jQuery对象,因为这是构造函数内部this)
三,如果selector是函数,那么直接调用$(document).ready()并且把函数作为ready函数的参数执行!

四,如果传入的selector是jQuery对象,那么把参数的jQuery对象的所有信息封装到新返回的对象中!

建议你首先阅读正则表达式的内容:

案例1:

扫描二维码关注公众号,回复: 1826092 查看本文章

[javascript]  view plain  copy
  1. $("<div></div><div></div>",{html:'abcn'}).appendTo($("body"));  
  2.                   
这种情况,虽然也是创建标签,而且第二个参数isPlainObject返回true,但是var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);正则表达式无法通过,最终jQuery什么也不做!
案例2:

[javascript]  view plain  copy
  1. $("<li>hello").appendTo($("body"))  

这种情况也是创建标签,但是经过match = rquickExpr.exec( selector );最后的结果仅仅获取到的match[1]是一个空的li标签,而不是<li>Hello</li>所以添加到body中的是一个空的li标签!

原文链接:https://blog.csdn.net/liangklfang/article/details/49465493

猜你喜欢

转载自blog.csdn.net/qq_38836118/article/details/80574670