1.入口結構html
( 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 ) { //具體代碼 }
抽離結構以下:node
( function() { })()
涉及到的知識jquery
js解析函數的規則windows
函數定義和函數表達式數組
function test(){ }
let test = function(){ }
js閉包閉包
//閉包舉例 function f(){ var a = 2; function g(){ console.log(a) }; return g; } f()();
綜合以上的內容,再來看一下剛纔抽離出來的代碼app
(function(){ })()
第一個括號有兩個做用:函數
第二個括號ui
2.第二個括號內的參數有哪些?this
(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { })
3.第一個括號內的函數作了什麼?
( function( global, factory ) { "use strict"; //判斷是否是在commonjs環境下,若是是就執行如下代碼 if ( typeof module === "object" && typeof module.exports === "object" ) { //判斷是否支持global.document module.exports = global.document ? factory( global, true ) : function( w ) { //不支持global.document時報錯 if ( !w.document ) { throw new Error( "jQuery requires a window with a document" ); } //報錯後返回jquery(w) return factory( w ); }; } else { //windows環境下執行這個代碼 factory( global ); } } )
4.判斷完環境後經過:factory( global );
跳轉到第二個括號的第二個參數內執行具體的內容
//總體的結構抽離以下 function( window, noGlobal ) { "use strict"; //具體的jquery內部代碼 if ( !noGlobal ) { //在window下能夠用如下方式調用 window.jQuery = window.$ = jQuery; } return jQuery; }
5.進入函數後,先定義了一些變量,函數和對象(能夠跳過先看下面的內容)
//定義了一些變量和方法 var arr = []; var document = window.document; var getProto = Object.getPrototypeOf; //數組方法簡寫 var slice = arr.slice; var concat = arr.concat; var push = arr.push; var indexOf = arr.indexOf; //對象方法簡寫 var class2type = {}; var toString = class2type.toString; var hasOwn = class2type.hasOwnProperty; var fnToString = hasOwn.toString; var ObjectFunctionString = fnToString.call( Object ); var support = {}; //定義函數 var isFunction = function isFunction( obj ) { return typeof obj === "function" && typeof obj.nodeType !== "number"; }; var isWindow = function isWindow( obj ) { return obj != null && obj === obj.window; }; function DOMEval( code, doc, node ) { doc = doc || document; var i, script = doc.createElement( "script" ); script.text = code; if ( node ) { for ( i in preservedScriptAttributes ) { if ( node[ i ] ) { script[ i ] = node[ i ]; } } } doc.head.appendChild( script ).parentNode.removeChild( script ); } function toType( obj ) { if ( obj == null ) { return obj + ""; } // Support: Android <=2.3 only (functionish RegExp) return typeof obj === "object" || typeof obj === "function" ? class2type[ toString.call( obj ) ] || "object" : typeof obj; } //定義對象 var preservedScriptAttributes = { type: true, src: true, noModule: true };
6.定義完上面的內容後,jQuery內部進行new對象,使得簡化使用操做
var version = "3.3.1", //在這裏jquery經過new新生成了對象簡化了使用時的操做 jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); },
7.new新對象時,jQuery.fn是什麼?
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 }
總結:看這部分源碼能夠知道jQuery.fn
就是jquery的原型對象
8.jQuery.fn.init( selector, context )
具體作了什麼?
//selector 選擇器,多是DOM對象、html字符串、jQuery對象 //context 選擇器選擇的範圍 //rootjQuery == $(document); init = jQuery.fn.init = function( selector, context, root ) { var match, elem; // 沒有傳選擇器直接返回 if ( !selector ) { return this; } root = root || rootjQuery; // 選擇器傳入的是字符串 if ( typeof selector === "string" ) { if ( selector[ 0 ] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { match = [ null, selector, null ]; } else { match = rquickExpr.exec( selector ); } if ( match && ( match[ 1 ] || !context ) ) { // HANDLE: $(html) -> $(array) if ( match[ 1 ] ) { context = context instanceof jQuery ? context[ 0 ] : context; jQuery.merge( this, jQuery.parseHTML( match[ 1 ], context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { // Properties of context are called as methods if possible if ( isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); // ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); } } } return this; // HANDLE: $(#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, $(...)) } else if ( !context || context.jquery ) { return ( context || root ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { this[ 0 ] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if ( isFunction( selector ) ) { return root.ready !== undefined ? root.ready( selector ) : // Execute immediately if ready is not present selector( jQuery ); } return jQuery.makeArray( selector, this ); };
總體看摸不着頭腦,抽離結構以下:
init = jQuery.fn.init = function( selector, context, root ) { if ( typeof selector === "string" ) { //選擇器類型是字符 }else if( selector.nodeType ){ //選擇器類型是節點 }else if( jQuery.isFunction( selector ) ){ //簡化$(document).ready(function(){}); } //返回結果。 return jQuery.makeArray( selector, this ); }
抽離完了要想理解這些內容,首先來看看jquery到底支持哪些選擇器selector?
1.$(document) 2.$('<div>') 3.$('.div') 4.$('#test') 5.$(function(){}) 6.$("input:radio", document.forms[0]); 7.$('input', $('div')) 8.$() 9.$("<div>", { "class": "test" }).appendTo("body");
接着一個一個分支的看,它是如何支持這些選擇器的,首先是typeof selector === "string"
if ( typeof selector === "string" ) { //傳入的是標籤類型,好比<p> if ( selector[ 0 ] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { // 將html儲存入match數組中,並與另外一個分支中的正則捕獲相對應 match = [ null, selector, null ]; } else { //放入正則進行匹配,結果類型是:[全匹配, <tag>, #id] //rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/ //匹配HTML標記和ID表達式 match = rquickExpr.exec( selector ); } // 若是match不爲空,而且match[1]也就是<tag>存在 if ( match && ( match[ 1 ] || !context ) ) { if ( match[ 1 ] ) { // 若是context是jQuery對象,則取其中的第一個DOM元素做爲context context = context instanceof jQuery ? context[ 0 ] : context; // 將經過parseHTML處理生成的DOM對象merge進jQuery對象 jQuery.merge( this, jQuery.parseHTML( match[ 1 ], //若是context存在而且是note節點,則context就是的頂級節點或自身,不然content=document context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { if ( isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); } else { this.attr( match, context[ match ] ); } } } return this; //若是是#id的形式,走這個分支進行處理 } else { //經過getEle方法得到DOM對象 將match[2]傳入,是由於#id的形式是在第二個捕獲組裏面儲存的。 elem = document.getElementById( match[ 2 ] ); // 若是該id元素存在 if ( elem ) { // 將該元素保存進jQuery對象數組當中,並設置其length值爲1 this[ 0 ] = elem; this.length = 1; } return this; } } else if ( !context || context.jquery ) { //若是context不存在或者context是jQuery對象 //經過檢測是否是有jquery屬性 // 進入Sizzle進行處理(複雜的選擇器) return ( context || root ).find( selector ); } else { //context存在而且context不是jQuery對象的狀況 先調用$(context),在進入Sizzle進行處理 return this.constructor( context ).find( selector ); } }
接着是selector.nodeType
分支
else if ( selector.nodeType ) { //直接將DOM元素存入jQuery對象並設置context和length this.context = this[0] = selector; this.length = 1; return this; }
最後是jQuery.isFunction( selector )
分支
else if ( jQuery.isFunction( selector ) ) { //簡化$(document).ready(function(){}); return rootjQuery.ready( selector ); }
分析了以上的分支,把jquery的選擇器分別帶進去走一下流程,首先是`3.$('div')
首先進入:
if ( typeof selector === "string" ) {}
接着進入下面的if分支:
if ( match && (match[1] || !context) ) { if ( match[1] ) { context = context instanceof jQuery ? context[0] : context; jQuery.merge( this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true ) ); } }
它進入了一個函數parseHTML()
jQuery.parseHTML = function( data, context, keepScripts ) { if ( typeof data !== "string" ) { return []; } if ( typeof context === "boolean" ) { keepScripts = context; context = false; } var base, parsed, scripts; if ( !context ) { if ( support.createHTMLDocument ) { context = document.implementation.createHTMLDocument( "" ); base = context.createElement( "base" ); base.href = document.location.href; context.head.appendChild( base ); } else { context = document; } } //var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); //匹配一個獨立的標籤 parsed = rsingleTag.exec( data ); scripts = !keepScripts && []; if ( parsed ) { return [ context.createElement( parsed[ 1 ] ) ]; } //未經過節點的字符串,則經過建立一個div節點,將字符串置入div的innerHTML parsed = buildFragment( [ data ], context, scripts ); if ( scripts && scripts.length ) { jQuery( scripts ).remove(); } return jQuery.merge( [], parsed.childNodes ); };
最後返回this也就是jQuery
return this;
再看一下4.$("#id")
首先進入:
if ( typeof selector === "string" ) {}
接着進入下面的else分支進行正則匹配:
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { //不知足 }else{ match = rquickExpr.exec( selector ); }
匹配完了再接着進入下面的else分支進行尋找添加:
if ( match[1] ) { //不知足 }else{ elem = document.getElementById( match[2] ); if ( elem ) { // 將該元素保存進jQuery對象數組當中,並設置其length值爲1 this.length = 1; this[0] = elem; } }
最後返回this也就是jQuery
return this;
今兒先看到這!!!!