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;
今儿先看到这!!!!