jQuery是目前最爲長壽的JS函數庫,還有日本up主小哥專門拍了一個視頻。雖然從網上看到的,都是唱衰jQuery的聲音,可是做爲一隻菜雞,我仍是從jQuery開始個人框架學習吧。api
在我這隻菜雞看來,jQuery令我感到恐懼的地方,是它的鏈式操做,不就是套娃麼。數組
鏈式操做的核心在於,jQuery返回的是一個jQuery對象,一個擁有jQuery所有功能的對象。框架
按照上述的說法,我大膽猜想,jQuery的本質是一個構造函數,雖然它不須要使用New
。那麼,就試試看復現jQuery的源碼吧~ide
window.jQuery = function(selector){
//要返回一個可操做的對象,因此jQuery是一個全局函數
const elements = document.querySelectorAll(selector);
//咱們要建立一個jQuery返回的jQuery對象api
const api ={
//拿addClass功能做爲例子,要注意addClass是api對象的功能
addclass(className){
for(let i=0; i<elements.length; i++){
elements[i].classList.add(className);
}
//return api; 運行結束以後返回一個能夠調用操做的對象
//由於是返回調用該函數的對象,因此能夠使用this
return this;
}
}
return api;
//由於調用jQuery函數的對象是window
//因此此處return this; 返回的是window對象
}
window.$ = window.jQuery;
//這樣,咱們的jQuery函數就能夠拿$符直接操做了
複製代碼
既然咱們找到了鏈式操做的返回對象,那麼咱們能夠更近一步,在調用函數以後直接返回一個對象,咱們的jQuery雖然不是一個構造函數,可是經過return,實現了構造對象的功能。函數
window.jQuery = function(selector){
//要返回一個可操做的對象,因此jQuery是一個全局函數
const elements = document.querySelectorAll(selector);
//咱們能夠直接返回一個對象
return{
addclass(className){
for(let i=0; i<elements.length; i++){
elements[i].classList.add(className);
}後返回一個能夠調用操做的對象
//由於是返回調用該函數的對象,因此使用this
return this;
},
//咱們新增一個find功能
find(selector,scope){
const arr = [];
for(let i=0;i<elements.length;i++){
//若是scope範圍節點存在,則能夠在範圍節點的範圍內搜索元素
arr = arr.concat(Arry.from(
(scope || document).querySelectorAll(selector)));
}
//問題來了,咱們的find若是返回查找結果的話,豈不是喪失了鏈式操做
return arr;
}
}
//調用jQuery函數的對象是window
//若是此處return this; 返回的是window對象
}
window.$ = window.jQuery;
複製代碼
在咱們簡化告終構的jQuery函數中,添加了新的功能find
。學習
find(selector,scope){
const arr = [];
for(let i=0;i<elements.length;i++){
//若是scope範圍節點存在,則能夠在範圍節點的範圍內搜索元素
arr = arr.concat(Array.from(
(scope || document).querySelectorAll(selector)));
}
//問題來了,咱們的find若是返回查找結果的話,豈不是喪失了鏈式操做
return arr;
}
複製代碼
通過find
查找後,咱們會得到一個新的數組arr,arr包含了咱們所須要的元素,可是直接返回arr,意味着鏈式操做會終止。ui
咱們不能繼續return this;
麼?this
若是繼續return this;
,咱們返回的是調用find
的對象。spa
咱們要怎麼繼續鏈式操做呢?prototype
讓arr成爲咱們return的jQuery對象。
要怎麼讓讓arr成爲咱們return的jQuery對象?
咱們能夠套娃,讓jQuery再調用一次arr,讓arr包裝成jQuery對象
✿✿ヽ(°▽°)ノ✿
可是咱們的jQuery只能接收字符串不是麼
find(selector,scope){
const arr = [];
for(let i=0;i<elements.length;i++){
//若是scope範圍節點存在,則能夠在範圍節點的範圍內搜索元素
arr = arr.concat(Array.from(
(scope || document).querySelectorAll(selector)));
}
//問題來了,咱們的find若是返回查找結果的話,豈不是喪失了鏈式操做
return jQuery(arr);
}
複製代碼
可是咱們的jQuery只能接收字符串不是麼
咱們能夠修改jQuery函數吖
window.jQuery = function(selectorOrArry){
let elements;
//const只支持在聲明時爲變量賦值,因此這裏採用let
if(selectorOrArry === "string"){
//咱們對輸入進行判斷,若是是字符串就當作選擇器
elements= document.querySelectorAll(selectorOrArry);
}else if(selectorOrArry instanceof Array) {
//若是輸入是數組,就進行套娃
elements= selectorOrArry;
}
return{
//這裏就是咱們所返回的jQuery元素
}
}
window.$ = window.jQuery;
複製代碼
至此,咱們實現了jQuery的基本鏈式套娃操做。
強大的jQuery鏈式套娃操做就這麼簡單嗎?不不不,咱們的套娃怎會如此善罷甘休。jQuery真正厲害的地方,在於提供了end()
,能夠返回上一次操做的jQuery對象。
既然end()
太騷了這麼厲害,咱們也要努力山寨一下。
window.jQuery = function(selectorOrArry){
let elements;
//const只支持在聲明時爲變量賦值,因此這裏採用let
if(selectorOrArry === "string"){
//咱們對輸入進行判斷,若是是字符串就當作選擇器
elements= document.querySelectorAll(selectorOrArry);
}else if(selectorOrArry instanceof Array) {
//若是輸入是數組,就進行套娃
elements= selectorOrArry;
}
return{
oldApi: selectorOrArry.oldApi,
//咱們給返回的jQuery對象新增oldApi屬性
end(){
//end只須要返回oldApi
return this.oldApi;
},
find(selector,scope){
const arr = [];
for(let i=0;i<elements.length;i++){
//若是scope範圍節點存在,則能夠在範圍節點的範圍內搜索元素
arr = arr.concat(
Array.from((scope || document).querySelectorAll(selector)));
}
arr.oldApi = this;
//咱們將須要存檔的jQuery對象做爲屬性增長到arr對象上
//雖然arr是數組,數組也是對象,也有屬性
return jQuery(arr);
//這樣,咱們在套娃的時候,就會將oldApi一併返回
}
}
}
window.$ = window.jQuery;
複製代碼
截止目前,咱們基本山寨完成了jQuery的套娃操做。可是若是每次操做都新增一個jQuery對象,也就意味着每一個對象都會重複建立一樣的對象屬性,佔據額外的空間。
若是引入對象的原型鏈,咱們的jQuery就更像一個構造函數了就能夠更高效了。
window.jQuery = function(selectorOrArry){
let elements;
if(selectorOrArry === "string"){
elements= document.querySelectorAll(selectorOrArry);
}else if(selectorOrArry instanceof Array) {
elements= selectorOrArry;
}
//咱們新建一個返回的jQuery對象:api,該對象能夠直接調用 jQuery.prototype
const api = Object.creat(jQuery.prototype);
//api須要有elements 和 oldApi屬性
api.elements = elements;
api.oldApi = selectorOrArry.oldApi;
return api;
}
window.$ = window.jQuery;
//設置jQuery的原型對象
jQuery.prototype = {
constructor: jQuery,
end(){return this.oldApi;},
find(selector,scope){
const arr = [];
for(let i=0;i<this.elements.length;i++){
//find函數不能直接調用jQuery函數內的對象,只能調用調用find屬性的對象的屬性
arr = arr.concat(
Array.from((scope || document).querySelectorAll(selector)));}
arr.oldApi = this;
return jQuery(arr); }
}
複製代碼
至此,咱們實現了jQuery的鏈式操做山寨源碼。讓咱們來欣賞一下jQuery源碼又是怎麼處理的:
(function(root){
var jQuery = function{
//返回對象,jQuery實現對象的建構以及原型的繼承
return new jQuery.prototype.init();
}
jQuery.prototype{
init(){ }
}
//原型共享
jQuery.prototype.init.prototype = jQuery.prototype;
root.$ = root.jQuery = jQuery;
})(this)
複製代碼