jQuery源碼解析(一):讀懂源碼結構、改變固有思惟方式

前端必備:熟讀框架源碼,今天來分析jQuery的源碼css

下載源碼

咱們想要讀一個框架的源碼,那第一步確定是下載源碼(這是廢話)。前端

地址:jQuery.js(本文代碼版本爲jquery3.4.1jquery

點擊上面地址,將代碼粘貼到本身的本地編輯器中,而後開始進行下一步工做。git

源碼結構

這一步,咱們分析代碼,研究代碼結構es6

咱們經過觀察源碼的結構,將代碼分割成了三個部分github

/*
    * 第一部分:匿名函數結構
    */
    /*!
     * jQuery JavaScript Library v3.4.1
     * https://jquery.com/
     *
     * Includes Sizzle.js
     * https://sizzlejs.com/
     *
     * Copyright JS Foundation and other contributors
     * Released under the MIT license
     * https://jquery.org/license
     *
     * Date: 2019-05-01T21:04Z
     */
    ( function( global, factory ) {

    	"use strict";
    
    	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 have a `window` with a `document`
    		// (such as Node.js), expose a 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 ) {
    /*
    * 第二部分:功能代碼(省略)
    */
    
    
    // 第三部分:返回
    // Register as a named AMD module, since jQuery can be concatenated with other
    // files that may use define, but not via a proper concatenation script that
    // understands anonymous AMD modules. A named AMD is safest and most robust
    // way to register. Lowercase jquery is used because AMD module names are
    // derived from file names, and jQuery is normally delivered in a lowercase
    // file name. Do this after creating the global so that if an AMD module wants
    // to call noConflict to hide this version of jQuery, it will work.
    
    // Note that for maximum portability, libraries that are not jQuery should
    // declare themselves as anonymous modules, and avoid setting a global if an
    // AMD loader is present. jQuery is a special case. For more information, see
    // https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
    
    if ( typeof define === "function" && define.amd ) {
    	define( "jquery", [], function() {
    		return jQuery;
    	} );
    }




    var

	// Map over jQuery in case of overwrite
	_jQuery = window.jQuery,

	// Map over the $ in case of overwrite
	_$ = window.$;

    jQuery.noConflict = function( deep ) {
    	if ( window.$ === jQuery ) {
    		window.$ = _$;
    	}

    	if ( deep && window.jQuery === jQuery ) {
    		window.jQuery = _jQuery;
    	}
	    return jQuery;
    };

    // Expose jQuery and $ identifiers, even in AMD
    // (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
    // and CommonJS for browser emulators (#13566)
    if ( !noGlobal ) {
    	window.jQuery = window.$ = jQuery;
    }




    return jQuery;
});
複製代碼

首先介紹一下匿名函數:編程

①匿名函數的結構json

/*
    * 第一個括號內爲匿名函數
    * 第二個括號用於調用函數,並傳參
    */
    (function(x,y){
        
    })(x,y)
複製代碼

②匿名函數的做用瀏覽器

  • 匿名函數最主要的做用是建立閉包【爲何要使用閉包】
  • 閉包指的是有權訪問另外一個函數做用域中的變量函數,也就是說函數內部再建立一個函數
for(var i = 0;i <= 10;i++) {
        setTimeout(() => {
            console.log(i);
        }, 1000)
    }
複製代碼

咱們執行上面這一段代碼,結果是輸出了1111,以下圖:bash

那爲何會這樣?咱們知道, for循環執行的速度要比 setTimeout快得多,因此當跳出for循環時才能夠執行到 setTimeout裏面的函數中( console.log(i)),因此執行上述代碼片斷,輸出的是咱們所看到的結果。

因此,如今咱們想輸出1-10,那咱們應該怎麼作?

通常有兩種方式:

I、一種是用es6中的聲明變量關鍵字let代替var,咱們來看一下效果

II、另外一種就是使用匿名函數

for(var i = 0;i <= 10;i++) {
        (function(i){
            setTimeout(function(){
                console.log(i);
            }, 1000)
        })(i)
    }
複製代碼

在這個方法中,咱們使用了匿名函數,匿名函數傳入參數i,並接收,這個時候也獲得了咱們想要的0-10的結果,以下圖

  • 爲何要使用匿名函數

①匿名函數能夠自動執行

②提供模塊化,將變量傳入,爲全局變量提供局部範圍,能夠減小查找時的開銷量等。

介紹完上述基本概念,下面進行代碼解析

代碼分析

1、第一部分

1.1 先來看第一大段註釋部分

這麼一段很長的註釋包括了:jQuery庫的版本以及地址、包含的庫Sizzle.js及官網地址,其中這個Sizzle.js是一個純JavaScript css選擇器引擎、版權說明、以及發佈日期

1.2 接下來看匿名函數部分

1.2.1 它傳入了兩個參數並接收

  • typeof window !== "undefined" ? window : this 這裏對瀏覽器是否支持window環境作了一個判斷,假如不支持,則傳入this對象

  • function( window, noGlobal ) {} 這裏傳入了一個函數,windownoGloabl做爲參數,其實這個函數就是jQuery的功能函數部分

  • 同時接收了上面兩個參數(global對象和factory函數)。

1.2.2 "use strict"

  • 這兩個單詞的意思是,使用了嚴格模式,即在嚴格模式下運行代碼(建議加上~)。

1.2.3 咱們看下面這一段代碼,if-else語句中判斷了module的類型,其實這段代碼的含義,就是咱們在使用jQuery框架的時候,是用了哪一種方式引入:

if ( typeof module === "object" && typeof module.exports === "object" ) {
		// 先省略
	} else {
		factory( global );
	}
複製代碼
  • 假如,咱們這樣用script標籤引入:

<script src="jQuery.js"></script>

這個時候,是檢測不到module的,也就是module的值爲undefined,那麼這個時候代碼天然而然的就執行了else語句,也就是直接調用了factory函數,並在函數中傳入this或是Windows對象。

  • 而後,咱們這樣用require引入jQuery
const jquery = require('jquery');
複製代碼

咱們能夠嘗試輸出一下module,獲得了這樣的結果

能夠看到, module再也不是 undefined,而是一個 json類型的數據,這個時候也就執行了 if-else語句中的 if條件下的語句

1.2.4 接下來,咱們看if條件下到底寫了些什麼?

// For CommonJS and CommonJS-like environments where a proper `window`
    // is present, execute the factory and get jQuery.
    // For environments that do not have a `window` with a `document`
    // (such as Node.js), expose a 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 );
    	};
複製代碼
  • 先看註釋
//對於CommonJS和相似於CommonJS的環境,其中有一個適當的window
    //當前,執行factory並得到jQuery。
    //對於沒有帶有document的window的環境
    //(例如Node.js),將工廠公開爲module.exports。
    //這強調了建立一個真正的window的必要性。
    //例如var jQuery = require("jQuery")(window);
複製代碼

這裏會判斷global裏面有沒有document屬性,若是有,就會執行factory函數,若是沒有就會拋出一個異常(jQuery requires a window with a document[jQuery須要一個帶有documentwindow]),這個時候一般是所處的瀏覽器環境不支持使用jQuery框架。

2、第三部分

咱們仔細觀察這一部分,就會發現:這一部分其實就是處理向外暴露jQuery和$標識符,代碼在這一部分判斷了不少種狀況,這裏的處理便於大衆使用jQuery框架。

3、第二部分

這一部分是功能代碼部分,大概有一萬多行的代碼,看這一部分的代碼,要學會人家的編程思想以及編寫一個框架的思惟方式

PS: 建議像我這樣沒有把jQuery API全記下來的羣體,能夠對照着API文檔看源碼,這樣可以更好的理解、瞭解功能的實現過程,也可以更好的記住這些API

總結

咱們經過上述的步驟,分析了jQuery源碼,瞭解了一個框架的基本結構,也知道了它爲何會這樣寫框架。咱們也就能夠經過上面的方法,不妨能夠嘗試寫出來一個屬於本身的庫~

對於仍是一個小白的我來說,寫出來的文章會有很差、甚至錯誤的地方,還但願你們積極指正~讓小白快速成長~

相關文章
相關標籤/搜索