首先,我們先去官網把JQ的js相關檔案download到本地,看著原始碼,仿照寫法,一步步實現並且理解jq的原理。
接著建立一個屬於自己的js檔案(取名為jquerMey-1.0.1js)。
這裡先說一下解析原始碼的幾個步驟:
-
學會分析組成及架構 => (JQ通過選擇器(字串)來檢索所有匹配的DOM,並且進行批量操作,同時能夠幫我們解決瀏覽器的相容問題。)
-
學會看英文註釋(不懂多用騰訊翻譯君[手動滑稽])
-
先減後刪
-
閱讀思考作者的語義
-
嘗試補全 好的,開搞吧!
首先創立一個html檔案,如圖:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jQuery原始碼解析</title>
</head>
<body>
<div class="box">這是一個div</div>
<span class="box">這是一個span</span>
<script src="jquery-3.3.1.js"></script>
<!--<script src="sizzle.js"></script>-->
<!--<script src="jquerMey-1.0.1.js"></script>-->
<script type="text/javascript">
var $eles = $('.box');
var $eles = jQuery('.box');
console.log($eles)
$eles.addClass('myFirst')
</script>
</body>
</html>
複製程式碼
可以看到,這邊jQuery.fn.init 輸出的是一個陣列,還有一系列方法。我們一步步來。
這邊先把JQ原始碼的所有東西都先刪一下,可以看到,定義一個匿名函式,建立 閉包。
// 定義一個匿名函式,馬上呼叫它,包起來呼叫的時候可以建立閉包
(function(global,factory) {
//記憶體中動態開闢了一塊空間來執行這個裡面的程式碼,對外是封閉的,可以訪問外面的變數
}(typeof window !== "undefined" ? window : this, function (window, noGlobal) {
/*這裡的三元判斷,除了BOM瀏覽器的執行環境還能執行在什麼環境中? =>node環境 (node執行在V8引擎中,主要用來做中介軟體)
中介軟體很多,架構與部署方面的中介軟體:webpack,grunt,gulp;功能方面的中介軟體:node.js(頁面靜態化) */
}));
複製程式碼
好,接著分析
// 定義一個匿名函式,馬上呼叫它,包起來呼叫的時候可以建立閉包
(function (global, factory) {
//記憶體中動態開闢了一塊空間來執行這個裡面的程式碼,對外是封閉的,可以訪問外面的變數
/*那麼除了BOM瀏覽器的執行環境還能執行在什麼環境中? =>
node環境 (node執行在V8引擎中,主要用來做中介軟體) 中介軟體很多,
架構與部署方面的中介軟體:webpack,grunt,gulp;功能方面的中介軟體:node.js(頁面靜態化) */
if (typeof module === "object" && typeof module.exports === "object") {
// For CommonJS and CommonJS-like environments where a proper `window`
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) {
}));
複製程式碼
寫到這裡,那麼這裡註釋說的CommonJS是什麼呢?這就涉及到了上面說的node了。
CommonJS是nodejs也就是伺服器端廣泛使用的模組化機制。 該規範的主要內容是,模組必須通過module.exports 匯出對外的變數或介面,通過 require() 來匯入其他模組的輸出到當前模組作用域中。
可以看到,這裡並沒有給factory()傳入第二個引數,預設為false,則會執行下面if的程式碼(即為BOM環境)。在if語句中,可以看到jQuery一定是核心程式碼,那麼jQuery到底是什麼呢?繼續看。
這裡的jQuery本質就是一個函式,jQuery有一個fn物件,並且fn有一個init函式。這裡的makeArrray本質是返回一個陣列。
往下看,可以看到這裡jQuery的fn物件其實就是jQuery的原型物件;接著我們找到init方法。
jQuery.fn = jQuery.prototype = {
init : function (selector, context) {
return jQuery.makeArray( selector, context );
}
};
jQuery.makeArray = function(selector, context){
var $eles = new Sizzle(selector, context);
return $eles;
}
複製程式碼
分析完jQuery.fn,我們看看makeArray。Sizzle.js檔案裡面有很多演算法方面的程式碼,我們先跳過,繼續分析程式碼。此時,我們用Chrome開啟html程式碼,可以看到,輸出如圖:(此時還沒有寫addClass函式所以報錯了)
jQuery.fn = jQuery.prototype = {
init : function (selector, context) {
return jQuery.makeArray( selector, context );
},
each: function (func) {
},
addClass : function (className) {
},
removeClass: function (className) {
}
};
jQuery.makeArray = function(selector, context){
var $eles = new Sizzle(selector, context);
$eles.prevObject = arguments.callee;
$eles.__proto__ = jQuery.fn
return $eles;
}
複製程式碼
繼續補全,這樣jQuery的 整體架構 就ok了,之後就是往裡面新增東西。
(比如往裡面新增addClass,removeClass,each方法)
jQuery.fn = jQuery.prototype = {
init : function (selector, context) {
return jQuery.makeArray( selector, context );
},
each: function (func) {
for (var i=0;i<this.length;i++) {
func.call(this,i,this[i]);
}
return this;
},
addClass : function (className) {
return this.each(function (index, element) {
element.className += " " + className
})
},
removeClass: function (className) {
return this.each(function (index, element) {
element.className = ""
})
}
};
複製程式碼
我們可以看到此時控制檯裡面已經有了我們新增的方法,讓我們來實驗一下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jQuery原始碼解析</title>
</head>
<style>
.myEleFirst {
display: block;
width: 100px;
height: 100px;
margin: 10px auto;
background: red;
}
button {
display: block;
width: 20px;
height: 20px;
margin: auto;
}
</style>
<body>
<div class="myEle">這是一個div</div>
<span class="myEle">這是一個span</span>
<button onclick="removeClass()"></button>
<!--<script src="jquery-3.3.1.js"></script>-->
<script type="text/javascript" src="sizzle.min.js"></script>
<script type="text/javascript" src="jquerMey-1.0.1.js"></script>
<script type="text/javascript">
var $eles = $(".myEle");
var $eles = jQuery(".myEle");
console.log($eles);
$eles.addClass('myEleFirst');
function removeClass() {
$eles.removeClass("myEleFirst")
}
</script>
</body>
</html>
複製程式碼
結果如圖:
附上全部程式碼:
/*!
* jqueMey JavaScript Library v1.0.1
*
* Includes Sizzle.js
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* Email: huangmiantong@126.com || v_mtonhuang@tencent.com
*
* Date: 2018-12-11T22:04Z
*/
// 定義一個匿名函式,馬上呼叫它,包起來呼叫的時候可以建立閉包
(function (global, factory) {
//在BOM瀏覽器的執行環境
/*那麼除了BOM瀏覽器的執行環境還能執行在什麼環境中? =>
node環境 (node執行在V8引擎中,主要用來做中介軟體) 中介軟體很多,
架構與部署方面的中介軟體:webpack,grunt,gulp;功能方面的中介軟體:node.js(頁面靜態化) */
//記憶體中動態開闢了一塊空間來執行這個裡面的程式碼
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) {
var
version = "1.0.1",
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
};
jQuery.fn = jQuery.prototype = {
init : function (selector, context) {
return jQuery.makeArray( selector, context );
},
each: function (func) {
for (var i=0;i<this.length;i++) {
func.call(this,i,this[i]);
}
return this;
},
addClass : function (className) {
return this.each(function (index, element) {
element.className += " " + className
})
},
removeClass: function (className) {
return this.each(function (index, element) {
element.className = ""
})
}
};
jQuery.makeArray = function(selector, context){
var $eles = new Sizzle(selector, context);
$eles.prevObject = arguments.callee;
$eles.__proto__ = jQuery.fn
return $eles;
}
// 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) {
//BOM一定有window物件
// jQuery一定是核心物件
window.jQuery = window.$ = jQuery;
}
return jQuery;
}));
複製程式碼
未完待續...