這篇文章主要介紹了關於jQuery原始碼之回呼函數的解析,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下
回呼函數
一、概念
回呼函數
是一個透過函數指標來呼叫執行的函數,如果你把一個函數的指標當作參數傳遞出去,那麼這個指標呼叫這個函數的時候,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。
好處:
使用回呼函數處理,程式碼就可以繼續進行其他任務,而無需空等。實際開發中,經常在javascript中使用非同步呼叫。
非同步回呼
$(document).ready(callback); $(document).on(‘click’,callback) $.ajax({ url: "aaron.html", context: document }).done(function() { //成功执行 }).fail(function() { //失败执行 ); $('#clickme').click(function() { $('#book').animate({ opacity: 0.25, left: '+=50', height: 'toggle' }, 5000, function() { // Animation complete. }); });
#同步
##
var test1 = function(callback) { //执行长时间操作 callback(); } test1(function() { //执行回调中的方法 }); 一个同步(阻塞)中使用回调的例子,目的是在test1代码执行完成后执行回调callback
#所以理解回呼函數最重要的2點:
1、一個回呼函數作為參數傳遞給另一個函數是,我們只是傳遞了函數定義。我們並沒有在參數中執行函數。我們並沒有傳遞像我們平常執行函數一樣帶有一對執行小括號()的函數
2、回呼函數並不會馬上被執行,它會在包含它的函數內的某個特定時間點被「回調」。
二、觀察者模式
在理解jquery的回呼物件之前我們先來學習觀察者模式(SB模式):
觀察者模式: 一個物件作為一個特定任務的觀察者
,當這個任務出發或執行完畢之後通知觀察者
(Subscriber)。 觀察者
也可以叫做訂閱者
,它指向被觀察者
(Publisher),當事件發生時,
- 會通知
- 觀察者
。
對於
所建立的
Callback對象,它的
add和
fire方法就是,其實就是基於發布訂閱
(Publish/Subscribe)的觀察者模式的設計。
// 模拟一下这种模式 function aa() { console.log('aa'); } function bb() { console.log('bb'); } var m_db = { Callbacks: [], add: function(fn) { this.Callbacks.push(fn); }, fire: function() { this.Callbacks.forEach(function(fn){ fn(); }) } } m_db.add(aa); m_db.add(bb); m_db.fire();
設計原理
開始建立一個存放回呼的數組,如this.callbacks= [] 加回時,將回呼push進this.callbacks,執行則遍歷this.callbacks執行回呼,也彈出1跟2了。當然這只是簡潔的設計,便於理解,整體來說設計的思路程式碼都是挺簡單的,那麼我們從簡單的設計深度挖掘下這種模式的優勢。 模式的實際使用
// 首先看一个场景 $.ajax({ url: '', .. }).done(function(data) { // 第一步处理数据 // 第二步处理DOM $('aaron1').html(data.a) $('aaron2').html(data.b) $('aaron3').html(data.c) // 其余处理 })
首先,所有的邏輯都寫在done方法裡面,這樣確實是無可厚非的,但是問題就是邏輯太複雜了。 Done裡面有
資料處理、
html渲染###、可能還有
它不同場景的業務邏輯。這樣如果是換做不同的人去維護程式碼,增加功能就會顯得很混亂而且沒有擴充性。<pre class="brush:php;toolbar:false">$.ajax({ url: '', .. }).done(function(data) { // 第一步处理数据 processData(data); // 第二步处理DOM processDom(data); // 其余处理 processOther(data); })</pre>
這樣看著時好一些了,透過同步執行來一次實現三個方面的處理,每一方面的處理都提取出來,但是這樣的寫法幾乎就是「就事論事」的處理,達不到抽象復用。<pre class="brush:php;toolbar:false">var m_cb = { callbacks: [], add: function(fn){ this.callbacks.push(fn); }, fire: function(data){ this.callbacks.forEach(function(fn){ fn(data); }) } } m_cb.add(function(data){ // 数据处理 }) m_cb.add(function(data){ // DOM处理 }) m_cd.add(function(data){ // 其余处理 }) $.ajax({ url: '', ... }).done(function(data){ m_cd.fire(data); })</pre>
這樣使用了觀察者模式之後是不是感覺好多了呢,設計該模式背後的主要動力是促進形成鬆散耦合
。在這種模式中,並不是一個物件呼叫另一個物件的方法,而是一個物件訂閱另一個物件的特定活動並在狀態改變後獲得通知。訂閱者也稱為觀察者,而被觀察的物件稱為發布者或主題。當發生了一個重要的事件時,發布者將會通知(呼叫)所有訂閱者並且可能經常以事件物件的形式傳遞訊息。總之、觀察者模式就是將函數/業務處理管理起來,當一定的事件觸發或時某一任務執行完畢後,一次執行。
三、$.Callbacks()
對於
$.Callbacks 所建立的
Callback
add和
fire方法就是,其實就是基於發布訂閱
(Publish/Subscribe)的觀察者模式的設計。
$.Callbacks
一般的開發者使用的較少,它的開發實作主要時為
$.ajax以及
$.deferred
- jQuery.Callbacks
- 是
jquery
在1.7版本之後加入的,是從1.6版中的###_Deferred###物件中抽離的,主要用來進行函數佇列的###add、remove、fire、lock###等操作,並提供###once、memory、unique、stopOnFalse###四個###option###進行一些特殊的控制。 ######這個函數常用的就是在事件觸發機制中,也就是觀察者設計模式的訂閱和發布模式中,$.Callbacks主要出現在ajax、deferred、queue中。 ############下面來仔細分析一下該方法的使用吧###
1、先来跑一下流程
function aa() { console.log('aa'); } function bb() { console.log('bb'); } var cb = $.Callbacks(); cb.add(aa); cb.add(bb); cb.fire(); // aa // bb
function fn1(value) { console.log(value); } function fn2(value) { fn1("fn2 says: " + value); return false; } var cb1 = $.Callbacks(); cb1.add(fn1); // 添加一个进入队列 cb1.fire('foo'); // 执行一下 // foo cb1.add(fn2); // 再添一个 cb1.fire('bar'); // 一次性执行 // bar // fn2 says: bar cb1.remove(fn2); // 移除一个 cb1.fire('111'); // 执行剩下的那一个 // 111
$.Callbacks()就是一个工厂函数。
jQuery.Callbacks() 的 API 列表如下:
callbacks.add() :回调列表中添加一个回调或回调的集合。 callbacks.disable() :禁用回调列表中的回调。 callbacks.disabled() :确定回调列表是否已被禁用。 callbacks.empty() :从列表中删除所有的回调。 callbacks.fire() :用给定的参数调用所有的回调。 callbacks.fired() :访问给定的上下文和参数列表中的所有回调。 callbacks.fireWith() :访问给定的上下文和参数列表中的所有回调。 callbacks.has() :确定列表中是否提供一个回调。 callbacks.lock() :锁定当前状态的回调列表。 callbacks.locked() :确定回调列表是否已被锁定。 callbacks.remove() :从回调列表中的删除一个回调或回调集合。
源码结构
jQuery.Callbacks = function(options) { // 首先对参数进行缓冲 options = typeof options === "string" ? (optionsCache[options] || createOptions(options)) : jQuery.extend({}, options); // 实现代码 // 函数队列的处理 fire = function() {} // 自身方法 self = { add: function() {}, remove: function() {}, has: function(fn) {}, empty: function() {}, disable: function() {}, disabled: function() {}, lock: function() {}, locked: function() {}, fireWith: function(context, args) {}, fire: function() {}, fired: function() {} }; return self; };
参数处理
// 处理通过空格分隔的字符串 var str = "once queue"; var option = {}; $.each(str.match(/\S+/g) || [], function (_index, item) { option[item] = true; }) console.log(option); // {once: true, queue: true}
Callbacks内部维护着一个List数组。这个数组用于存放我们订阅的对象,它是通过闭包来实现长期驻存的。添加回调时,将回调push进list,执行则遍历list执行回调。
Callbacks
有4个参数。
once
的作用是使callback
队列只执行一次。
var callbacks = $.Callbacks('once'); callbacks.add(function() { alert('a'); }) callbacks.add(function() { alert('b'); }) callbacks.fire(); //输出结果: 'a' 'b' callbacks.fire(); //未执行
// 来看一下具体怎么实现 // jQuery是在执行第一个fire的时候直接给清空list列表了,然后在add的地方给判断下list是否存在,从而达到这样的处理 function Callbacks(options){ var list = []; var self = {}; self: { add: function(fn){ list.push(fn); }, fire: function(data){ this.list.forEach(function(item){ item(data); }) if(options == 'once') { list = undefined; } } } return self; }
// $jQuery.Callbacks的处理,在fire中调用了 self.disable(); 方法 // 禁用回调列表中的回调。 disable: function() { list = stack = memory = undefined; return this; }
memory 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调
function fn1(val) { console.log('fn1 says ' + val); } function fn2(val) { console.log('fn2 says ' + val); } function fn3(val) { console.log('fn3 says ' + val); } var cbs = $.Callbacks('memory'); cbs.add(fn1); cbs.fire('foo'); // fn1 says foo console.log('..........') cbs.add(fn2); // 这里在添加一个函数进入队列的同时,就立马执行了这个 回调了 cbs.fire('bar'); // fn2 says foo 这个东东比较特殊~ // fn1 says bar // fn2 says bar console.log('..........') cbs.add(fn3); cbs.fire('aaron'); // fn3 says bar // fn1 says aaron // fn2 says aaron // fn3 says aaron
// 需要解决的问题一个就是如何获取上一个参数,以及add后的执行 function Callbacks(options) { var list = []; var self; var firingStart; var memory; function _fire(data) { memory = options === 'memory' && data; firingIndex = firingStart || 0; // firingStart = 0; firingLength = list.length; for (; list && firingIndex
Unique:确保一次只能添加一个回调(所以在列表中没有重复的回调)
function fn1(val) { console.log('fn1 says ' + val); } var callbacks = $.Callbacks( "unique" ); callbacks.add( fn1 ); callbacks.add( fn1 ); // repeat addition callbacks.add( fn1 ); callbacks.fire( "foo" );
stopOnFalse: 当一个回调返回false 时中断调用
function fn1(value) { console.log(value); return false; } function fn2(value) { fn1("fn2 says: " + value); return false; } var callbacks = $.Callbacks("stopOnFalse"); callbacks.add(fn1); callbacks.fire("foo"); callbacks.add(fn2); callbacks.fire("bar"); // foo // bar
$.callback()的源码
jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) //通过字符串在optionsCache寻找有没有相应缓存,如果没有则创建一个,有则引用 //如果是对象则通过jQuery.extend深复制后赋给options。 options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options ); var // Last fire value (for non-forgettable lists) memory, // 最后一次触发回调时传的参数 // Flag to know if list was already fired fired, // 列表中的函数是否已经回调至少一次 // Flag to know if list is currently firing firing, // 列表中的函数是否正在回调中 // First callback to fire (used internally by add and fireWith) firingStart, // 回调的起点 // End of the loop when firing firingLength, // 回调时的循环结尾 // Index of currently firing callback (modified by remove if needed) firingIndex, // 当前正在回调的函数索引 // Actual callback list list = [], // 回调函数列表 // Stack of fire calls for repeatable lists stack = !options.once && [],// 可重复的回调函数堆栈,用于控制触发回调时的参数列表 // Fire callbacks// 触发回调函数列表 fire = function( data ) { //如果参数memory为true,则记录data memory = options.memory && data; fired = true; //标记触发回调 firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; //标记正在触发回调 firing = true; for ( ; list && firingIndex -1 ) { list.splice( index, 1 ); // Handle firing indexes // 在函数列表处于firing状态时,最主要的就是维护firingLength和firgingIndex这两个值 // 保证fire时函数列表中的函数能够被正确执行(fire中的for循环需要这两个值 if ( firing ) { if ( index -1 : !!( list && list.length ); }, // Remove all callbacks from the list // 从列表中删除所有回调函数 empty: function() { list = []; firingLength = 0; return this; }, // Have the list do nothing anymore // 禁用回调列表中的回调。 disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? // 列表中否被禁用 disabled: function() { return !list; }, // Lock the list in its current state // 锁定列表 lock: function() { stack = undefined; if ( !memory ) { self.disable(); } return this; }, // Is it locked? // 列表是否被锁 locked: function() { return !stack; }, // Call all callbacks with the given context and arguments // 以给定的上下文和参数调用所有回调函数 fireWith: function( context, args ) { if ( list && ( !fired || stack ) ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; //如果正在回调 if ( firing ) { //将参数推入堆栈,等待当前回调结束再调用 stack.push( args ); } else {//否则直接调用 fire( args ); } } return this; }, // Call all the callbacks with the given arguments // 以给定的参数调用所有回调函数 fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once // // 回调函数列表是否至少被调用一次 fired: function() { return !!fired; } }; return self; };
未完待续~~
以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!
相关推荐:
以上是jQuery源碼之回呼函數的解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JavaScript起源於1995年,由布蘭登·艾克創造,實現語言為C語言。 1.C語言為JavaScript提供了高性能和系統級編程能力。 2.JavaScript的內存管理和性能優化依賴於C語言。 3.C語言的跨平台特性幫助JavaScript在不同操作系統上高效運行。

JavaScript在瀏覽器和Node.js環境中運行,依賴JavaScript引擎解析和執行代碼。 1)解析階段生成抽象語法樹(AST);2)編譯階段將AST轉換為字節碼或機器碼;3)執行階段執行編譯後的代碼。

Python和JavaScript的未來趨勢包括:1.Python將鞏固在科學計算和AI領域的地位,2.JavaScript將推動Web技術發展,3.跨平台開發將成為熱門,4.性能優化將是重點。兩者都將繼續在各自領域擴展應用場景,並在性能上有更多突破。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。