前端必備:熟讀框架源碼,今天來分析
jQuery
的源碼css
咱們想要讀一個框架的源碼,那第一步確定是下載源碼(這是廢話)。前端
地址:jQuery.js(本文代碼版本爲jquery3.4.1
)jquery
點擊上面地址,將代碼粘貼到本身的本地編輯器中,而後開始進行下一步工做。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)
}
複製代碼
咱們執行上面這一段代碼,結果是輸出了11
個11
,以下圖:bash
for
循環執行的速度要比
setTimeout
快得多,因此當跳出for循環時才能夠執行到
setTimeout
裏面的函數中(
console.log(i)
),因此執行上述代碼片斷,輸出的是咱們所看到的結果。
因此,如今咱們想輸出1-10,那咱們應該怎麼作?
通常有兩種方式:
I、一種是用es6
中的聲明變量關鍵字let
代替var
,咱們來看一下效果
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 ) {}
這裏傳入了一個函數,window
和noGloabl
做爲參數,其實這個函數就是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 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
須要一個帶有document
的window
]),這個時候一般是所處的瀏覽器環境不支持使用jQuery
框架。
2、第三部分
咱們仔細觀察這一部分,就會發現:這一部分其實就是處理向外暴露jQuery和$標識符,代碼在這一部分判斷了不少種狀況,這裏的處理便於大衆使用jQuery
框架。
3、第二部分
這一部分是功能代碼部分,大概有一萬多行的代碼,看這一部分的代碼,要學會人家的編程思想以及編寫一個框架的思惟方式
PS: 建議像我這樣沒有把jQuery API
全記下來的羣體,能夠對照着API
文檔看源碼,這樣可以更好的理解、瞭解功能的實現過程,也可以更好的記住這些API
。
咱們經過上述的步驟,分析了jQuery源碼,瞭解了一個框架的基本結構,也知道了它爲何會這樣寫框架。咱們也就能夠經過上面的方法,不妨能夠嘗試寫出來一個屬於本身的庫~
對於仍是一個小白的我來說,寫出來的文章會有很差、甚至錯誤的地方,還但願你們積極指正~讓小白快速成長~