jQuery 源碼解析代碼及更多學習乾貨: 猛戳GitHub前端
本篇代碼爲git
my-jQuery 1.0.2.jsgithub
my-jQuery 1.0.3.jsajax
建議下載源碼而後據文章思路學習,最好本身邊思考邊多敲幾遍。設計模式
1.$.callbacks
用於管理函數隊列。
2.經過add()
添加處理函數到隊列中,經過fire去執行這些函數。
3.$.callbacks
是在jQuery內部使用的,如爲.ajax,$.Deffed等組件提供基礎功能函數。它也能夠在相似功能的一些組件中,如本身開發的插件數組
$.callbacks
,獲取實例add()
向內部隊列添加函數fire()
依次找到並執行隊列裏的函數var cb = $.callbacks();
cb.add(function(){
console.log('add one');
});
cb.add(function(){
console.log('add two');
});
cb.add(function(){
console.log('add three');
});
cb.fire()
//依次輸出
add one
add two
add three
複製代碼
callbacks經過字符串參數的形式,支持四種特定的功能bash
once
// once關鍵字
var cbOne = $.Callbacks('once');
cbOne.add(function(){
console.log("this is cbOne1");
});
cbOne.add(function(){
console.log("this is a cbOne2");
});
// 只輸出執行一次,後面調用都不生效
cbOne.fire();
cbOne.fire();
執行結果:
this is cbOne1
this is a cbOne2
複製代碼
unique
// unique
var cbUnique = $.Callbacks('unique');
function demo(){
console.log("this is a cbUnique");
}
cbUnique.add(demo,demo);
cbUnique.fire();
// 輸出了一次
this is a cbUnique
複製代碼
stopOnFalse
// stopOnFalse 關鍵字
// 不加關鍵字的狀況
var cbDemo = $.Callbacks();
cbDemo.add(function(){
console.log("this is cbDemo 1");
return false;
},function(){
console.log("this is cbDemo 2");
})
cbDemo.fire();
輸出:
this is cbDemo 1
this is cbDemo 2
// 加關鍵字的狀況
var cbStopOnFalse = $.Callbacks('stopOnFalse');
cbStopOnFalse.add(function(){
console.log("this is a cbStopOnFalse 1");
return false;
},function(){
console.log("this is a cbStopOnFalse 2");
});
cbStopOnFalse.fire();
輸出:
this is a cbStopOnFalse 1
複製代碼
memory
// 參數 memory
// 不加參數的狀況
var cbNoMemory = $.Callbacks();
cbNoMemory.add(function(){
console.log("this is a cbNoMemory 1");
});
cbNoMemory.fire();
輸出:
this is a cbNoMemory 1
cbNoMemory.add(function (){
console.log("this is a cbNoMemory 2");
});
// 添加參數的狀況
var cbMemory = $.Callbacks('memory');
cbMemory.add(function(){
console.log("this is a cbMemory 1");
});
cbMemory.fire();
輸出:
this is a cbMemory 1
this is a cbMemory 2
cbMemory.add(function(){
console.log("this is a cbMemory 2");
})
複製代碼
1.事件一般與函數配合使用,這樣就能夠經過發生的事件來驅動函數的執行.app
原則:一個事件對應一個事件函數 在一個事件對應多個函數的狀況下,後者會覆蓋掉前者。dom
問題: 那麼咱們可否有一種方案來改變一對一的事件模型呢?函數
解決方案: 把事件放到一個數組裏,而後經過遍歷數組依次執行的方式來達到一對多的事件模型。
// 一對多事件模型
function one(){
console.log("one");
};
function two(){
console.log("two");
};
function three(){
console.log("three");
};
function four(){
console.log("four");
};
var clickCallBack = [one,two,three,four];
// 在body中定義一個button <button id="btn">按鈕</button>
$("#btn").click(function(){
var _this = this;
clickCallBack.forEach(function(fn){
fn.call(_this);
})
});
// 輸出結果:
one
three
three
four
複製代碼
2.Callbacks 不只僅是一個數組,能夠把它當作一個容器。
上面咱們已經經過jQuery來調用Callbacks的API並輸出了內容,根據兩個方法,add(),fire()
及四個參數,"once","unique","menory","stopOnfalse"
,的相關特性咱們開始反推實現過程.
首先是add()
方法:將穿過來的options先把他們轉爲真數組,而後將數組遍歷出來挑選出類型爲"Function"的數據,將數據添加到一個空數組中,等待執行。
僅是代碼片斷,完整代碼下載地址: 源碼下載
核心代碼片斷:
add:function(){
// Array.prototype.slice.call(arguments 僞數組轉真數組
var args = Array.prototype.slice.call(arguments);
start = list.length;
// 遍歷args 找出裏面的Function
args.forEach(function(fn){
// 檢索fn是是不是Function
if (toString.call(fn) === "[object Function]") {
// unique 不存在 且fn在list中 那麼能夠把fn添加到隊裏中
list.push(fn);
}
}
});
複製代碼
fire()
方法:fire其實就是把添加到隊列中的方法依次按規則輸出執行,須要一箇中間件fireWith提供上下文。
核心代碼:
var fire = function(data){
index = 0;
length = list.length;
// 遍歷循環list
for(; index < length; index++){
// 經過遍歷查找list[index]的值爲false 且options有stopOnfalse這個參數時遍歷終止返回
if (list[index].apply(data[0],data[1]) == false){
break;
}
}
}
複製代碼
// 定義一個上下文綁定函數
fileWith:function(context,arguments){
var args = [context,arguments];
fire(args);
}
複製代碼
fire:function(){
self.fileWith(this,arguments);
},
複製代碼
到此以上代碼能夠實現 add()
方法和fire()
方法,其次咱們在考慮四種參數的狀況,首先咱們先考慮 stopOnfalse
的狀況.
stopOnfalse這個參數生效的階段是在調用fire()方法後執行 add()
添加的隊列函數中是否有返回false的狀況,因此首先咱們應該想到在fire()這個方法裏作文章.
思路:直接在遍歷方法的時候來斷定optionss是否有 stopOnfalse
參數若是有立馬退出.
核心代碼:
var fire = function(data){
// memory
memory = options.memory && data;
// 爲了防止memory再次調用一次定義了starts
index = 0;
length = list.length;
// 遍歷循環list
for(; index < length; index++){
// 經過遍歷查找list[index]的值爲false 且options有stopOnfalse這個參數時遍歷終止返回
if (list[index].apply(data[0],data[1]) == false && options.stopOnfalse){
break;
}
}
複製代碼
once 參數生效的狀況是,當once
存在執行第一次完成後,若是還有fire()
方法,那麼就直接退出不執行.
思路:首先明白受影響階段是fire()
, 定義一個參數來記錄第一次執行fire()
的方法,而後在調用執行fire()
這個方法判斷是否傳入有 once
參數若是有,那麼就不會再去執行fire()
方法.
核心代碼:
var fire = function(data){
index = 0;
length = list.length;
startAdd = true;// 用來記錄fire()方式是否執行 便於"once"方法操做
// 遍歷循環list
for(; index < length; index++){
// 經過遍歷查找list[index]的值爲false 且options有stopOnfalse這個參數時遍歷終止返回
if (list[index].apply(data[0],data[1]) == false && options.stopOnfalse){
break;
}
}
}
複製代碼
// 定義一個上下文綁定函數
fileWith:function(context,arguments){
var args = [context,arguments];
// 非fire作限制調用
if(!options.once || !startAdd) {
fire(args);
}
},
複製代碼
memory這個參數生效的狀況是,若是執行 fire()
方法後,還存在 add()
的方法,那麼後面的 add()
方法依然有效。
思路: 首先要搞明白memory在哪一個階段會受影響,在add()
階段和fire(
)階段都有影響,add()
階段要記錄傳入的options是否有memory這個參數,其次在執行fire()
的階段,主要是要記錄住它的index值。
核心代碼:
var fire = function(data){
// memory
memory = options.memory && data;
// 爲了防止memory再次調用一次定義了memoryStarts
index = memoryStarts || 0;
start = 0;
length = list.length;
startAdd = true; // 用來記錄fire()方式是否執行 便於"once"方法操做
// 遍歷循環list
for(; index < length; index++){
// 經過遍歷查找list[index]的值爲false 且options有stopOnfalse這個參數時遍歷終止返回
if (list[index].apply(data[0],data[1]) == false && options.stopOnfalse){
break;
}
}
}
複製代碼
// memory
if (memory) {
memoryStarts = start;
fire(memory);
}
複製代碼
最難的是 unique 這個參數.
unique
這個參數生效的狀況是,同一個方法被add()
屢次,僅執行一次改方法。
思路: unique影響階段是add()
時候,全部咱們在這裏作攔截操做是最好的,所以咱們在add()
的時候作判斷若是存在 unique
這個參數,那麼咱們就不讓一樣的這個方法push到隊裏中,沒有添加到隊列,那麼咱們就不會再次執行這個方法啦.
兩種方法:
(1).經過數組的[].indexOf.call()
來查看是否存在於數組中,不存在返回-1.
(2).能夠用ES6的set進行過濾重複值
咱們採用方法一來完成此操做。
核心代碼:
// 添加 方法
add:function(){
// Array.prototype.slice.call(arguments 僞數組轉真數組
var args = Array.prototype.slice.call(arguments);
start = list.length;
// 遍歷args 找出裏面的Function
args.forEach(function(fn){
// 檢索fn是是不是Function
if (toString.call(fn) === "[object Function]") {
// unique 不存在 且fn在list中 那麼能夠把fn添加到隊裏中
// 處理 unique 參數
if(!options.unique || !self.has(fn,list)) {
list.push(fn);
}
}
});
複製代碼
has:function(fn,array){
return arr = jQuery.inArray(fn,array) > -1;
}
複製代碼
jQuery.inArray = function (elem,arr){
return arr == null?-1:[].indexOf.call(arr,elem);
}
複製代碼
至此,大功告成!! 完成了Callbacks()
實現原理剖析,你是學到了呢?
jQuery 源碼剖析 系列目錄地址:猛戳GitHub
jQuery 源碼剖析 系列預計寫十篇左右,旨在加深對原生JavaScript 部分知識點的理解和深刻,重點講解 jQuery核心功能函數、選擇器、Callback 原理、延時對象原理、事件綁定、jQuery體系結構、委託設計模式、dom操做、動畫隊列等。 若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。若是喜歡或者有所啓發,歡迎 star⭐️,對做者也是一種鼓勵。
關注公衆號回覆:學習 領取前端最新最全學習資料,也能夠進羣和大佬一塊兒學習交流