1. 程式人生 > >jQuery原始碼學習(二)

jQuery原始碼學習(二)

回撥物件Callbacks

回撥物件Callbacks就是用來管理回撥函式佇列的。

引數說明

它提供幾個便捷的處理引數

  • once: 確保這個回撥列表只執行一次
  • memory: 保持以前的值,將新增到這個列表的後面的最新的值立即執行呼叫任何回撥
  • unique: 確保一次只能新增一個回撥(所以在列表中沒有重複的回撥).
  • stopOnFalse: 當一個回撥返回false 時中斷呼叫
    once和stopOnFalse作用於fire
    memory和unique作用於add
    once在原始碼中的實現:
// 以給定的上下文和引數呼叫所有回撥函式
fireWith: function
( context, args ) {
//第二次觸發的時候fired為true //stack = !options.once && [] //!fired為false //如果沒有設定once,!options.once && []為true 則再次觸發fire( args ); //如果設定了once,!options.once && []為false 則不會再次觸發fire( args ); if ( list && ( !fired || stack ) ) { args = args || []; //args.slice是判斷args是不是陣列
args = [ context, args.slice ? args.slice() : args ]; if ( firing ) {//如果正在回撥 //將引數推入堆疊,等待當前回撥結束再呼叫 stack.push( args ); } else {//否則直接呼叫 fire( args ); } } return
this; }

memory在原始碼中的實現:

// 存在memory就會再次觸發fire
else if ( memory ) {
//如果options.memory為true,則將memory做為引數,應用最近增加的回撥函式
        firingStart = start;
        fire( memory );
                    }

unique在原始碼中的實現:
options.unique->如果沒有設定unique,不管list中有沒有該回調都可以新增
options.unique->如果設定了unique,只有list中沒有才可以新增該回調

if ( !options.unique || !self.has( arg ) ) {//確保是否可以重複
        list.push( arg );
    }

stopOnFalse在原始碼中的實現:

//data[ 0 ]是執行環境
//data[ 1 ]就是fire中的引數
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
    //阻止未來可能由於add所產生的回撥
    memory = false; 
    break;//由於options.stopOnFalse設定為true,所以當有回撥函式返回值為false時退出迴圈
                }

once_demo:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>once</title>
</head>
<body>
    <script type="text/javascript" src="../jquery-2.1.1.js"></script>
    <script type="text/javascript">
        var cb = $.Callbacks("once");
        function aaa(){
            console.log(1);
        }
        function bbb(){
            console.log(2);
        }
        cb.add(aaa);
        cb.add(bbb);
        cb.fire();//輸出1
        cb.fire();//不執行
    </script>
</body>
</html>

memory_demo:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>memory</title>
</head>
<body>
    <script type="text/javascript" src="../jquery-2.1.1.js"></script>
    <script type="text/javascript">
        var cb = $.Callbacks("memory");
        function aaa(){
            console.log(1);
        }
        function bbb(){
            console.log(2);
        }
        cb.add(aaa);
        cb.fire();
        cb.add(bbb);//由於memory有記憶功能,後新增的也能觸發
    </script>
</body>
</html>

unique_demo:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Unique</title>
</head>
<body>
    <script type="text/javascript" src="../jquery-2.1.1.js"></script>
    <script type="text/javascript">
        var cb = $.Callbacks("unique");
        function aaa(){
            console.log(1);
        }
        cb.add(aaa);//有效
        cb.add(aaa);//新增不進去
        cb.fire();//輸出 1
    </script>
</body>
</html>

原始碼處理解析:

if ( !options.unique || !self.has( arg ) ) { //確保是否可以重複
    list.push( arg );
}                

如果options.unique為true,並且回撥列表中不包含arg,則將arg新增到回撥列表中
stopOnFalse_demo:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>stopOnFalse</title>
</head>
<body>
    <script type="text/javascript" src="../jquery-2.1.1.js"></script>
    <script type="text/javascript">
        var cb = $.Callbacks("stopOnFalse");
        function aaa(){
            console.log(1);
            return false;
        }
        function bbb(){
            console.log(2);
        }
        cb.add(aaa);//執行輸出1
        cb.add(bbb);//不執行
        cb.fire();
    </script>
</body>
</html>

混合引數_demo:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>混合引數</title>
</head>
<body>
    <script type="text/javascript" src="../jquery-2.1.1.js"></script>
    <script type="text/javascript">
        var cb = $.Callbacks("once memory");
        function aaa(){
            console.log(1);
        }
        function bbb(){
            console.log(2);
        }
        cb.add(aaa);
        cb.fire();//執行輸出1 2
        cb.add(bbb);
        cb.fire();//因為once,不執行
    </script>
</body>
</html>

方法介紹

根據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() 從回撥列表中的刪除一個回撥或回撥集合。

延遲物件Deferred

延遲物件是基於回撥物件來實現的,用來實現非同步的統一管理
when()是用來輔助延遲物件的
大體框架如下:

jQuery.extend({//兩個工具方法
            Deferred:function(){},
            when : function(){}
        });

$.Deferred()

有三組對應關係:

  • resolve->done
  • reject->fail
  • notify->progress

前兩組對應關係,分別對應相應的狀態變化:

  • pending->resolved
  • pending->rejected
    一旦發生兩種狀態變化中的一種,則狀態變為不可變
<body>
    <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
    <script type="text/javascript">
    var cb = $.Callbacks();
    setTimeout(function(){
        alert(111);
        cb.fire();
    },1000);
    //add先把這個函式存到數組裡
    //當呼叫fire的時候再觸發
    cb.add(function(){
        alert(222);
    });
    </script>
    <script type="text/javascript">
    //延遲物件-實現對非同步的統一管理
    var dfd = $.Deferred();//延遲物件
    setTimeout(function(){
        alert(111);
        dfd.resolve();
    },1000);
    dfd.done(function(){
        alert(222);
    });
    </script>
    <script type="text/javascript">
    setTimeout(function(){
        alert(111);
    },1000);
    //定時器就是非同步的
    //延遲物件可以實現從上往下的順序
    //先彈出1,再彈出2
    alert(222);
    </script>
    <script type="text/javascript">
    var dfd = $.Deferred();
    setTimeout(function(){
        alert(111);
        dfd.reject();
    },1000);
    dfd.fail(function(){
        alert(222);
    });
    </script>
    <script type="text/javascript">
    var dfd = $.Deferred();
    setTimeout(function(){
        alert(111);
        dfd.notify();
    },1000);
    dfd.progress(function(){
        alert(222);
    });
    </script>
</body>
<body>
    <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
    <script type="text/javascript">
    var def = $.Deferred();
    setInterval(function(){
        alert(111);
        def.resolve();
        // def.reject();
        // def.notify();
    },1000);
    def.done(function(){
        alert("成功");//只會彈一次成功  
        //因為原始碼中設定了jQuery.Callbacks("once memory")
    }).fail(function(){
        alert("失敗");//只會彈一次失敗  
        //因為原始碼中設定了jQuery.Callbacks("once memory")
    }).progress(function(){
        alert("進度中");//每隔一秒都會彈出
    });
    </script>
</body>

deferred和promise的區別

Deferred物件和promise物件都是延遲物件

Deferred

  • resolve
  • reject
  • notify
  • state
  • always
  • then
  • promise
  • pipe
  • done
  • fail
  • progress

promise物件

  • state
  • always
  • then
  • promise
  • pipe
  • done
  • fail
  • progress

通過上面的列舉,我們可以看出:deferred比promise多了resolve、reject和notify

<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
function aaa(){
    var dfd = $.Deferred();
    setTimeout(function(){
        dfd.resolve();//不會觸發
    },1000);
    return dfd;
}
var newDfd = aaa();
newDfd.done(function(){
    alert("成功");
}).fail(function(){
    alert("失敗");//彈出失敗
});
newDfd.reject();//會先觸發
</script>
</body>

dfd.promise():延遲物件呼叫promise()方法,返回一個promise物件
promise物件中沒有reject()方法
這裡為了阻止延遲物件的狀態在外部被改變,需要這樣來呼叫dfd.promise()。它的作用是,在原來的deferred物件上返回promise物件,後者只開放與改變執行狀態無關的方法(比如done()方法和fail()方法),遮蔽與改變執行狀態有關的方法(比如resolve()方法和reject()方法),從而使得執行狀態不能被改變。
原始碼中是這樣實現的:
dfd.promise()這裡引數為空,所以這裡就返回promise物件

promise: function( obj ) {
    return obj != null ? jQuery.extend( obj, promise ) : promise;
                }
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
function aaa(){
    var dfd = $.Deferred();
    setTimeout(function(){
        dfd.resolve();
    },1000);
    return dfd.promise();
}
var newDfd = aaa();
newDfd.done(function(){
    alert("成功");//彈出成功
}).fail(function(){
    alert("失敗");
});
newDfd.reject();//會報錯  promise物件沒有改變狀態的方法
</script>
</body>

always

<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        var dfd = $.Deferred();
        setTimeout(function(){
            //dfd.resolve();
            dfd.reject();
        },1000);
        //不管是resolve還是reject,always總會執行
        dfd.always(function(){
            alert("總會彈出");
        });
    </script>
</body>

原始碼中是這樣寫的:

always: function() {
//總會觸發是因為既有done又有fail
deferred.done( arguments ).fail( arguments );
            return this;
            //return this表示可以進行鏈式操作
                }

state

返回狀態
resovled或者rejected

<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
    function aaa(){
        var dfd=$.Deferred();
        alert(dfd.state());//pending
        setTimeout(function(){
            dfd.resolve();//resolve()之後,狀態變為resolved
            alert(dfd.state());//resolved
        },1000);
        return dfd;
    }
    var newDfd=aaa();
    newDfd.done(function(){
        alert('成功');
    }).fail(function(){
        alert('失敗');
    });
</script>
</body>

then

<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        var dfd = $.Deferred();
        setTimeout(function(){
            //dfd.resolve();
            dfd.reject();
        },1000);
        //then中對應三種狀態
        dfd.then(function(){
            alert("成功");
        },function(){
            alert("失敗");
        },function(){
            alert("進行中");
        });
    </script>
</body>

pipe

// then和pipe程式碼一樣,功能不一樣
// pipe可以把延遲物件變長
promise.pipe = promise.then;
<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        var dfd = $.Deferred();
        setTimeout(function(){
            //dfd.resolve();
            dfd.resolve('hello');//可以接收引數傳遞
        },1000);
        var newDfd = dfd.pipe(function(){
            return arguments[0]+'你好';
        });
        newDfd.done(function(){
            alert(arguments[0]);//hello你好
        });
    </script>
</body>

$.when()

when是用來輔助延遲物件的工具方法

<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        function aaa(){
            var dfd = $.Deferred();
            //dfd.resolve();
            dfd.reject();
            return dfd;
        }
        function bbb(){
            var dfd = $.Deferred();
            dfd.resolve();
            //dfd.reject();
            return dfd;
        }
        /*
        這樣寫只有aaa()和bbb()同時完成觸發才彈出,但是任意一個未完成就可以觸發fail()
         */
        //when是針對多個延遲物件的
        $.when(aaa(),bbb()).done(function(){
            alert('成功');
        }).fail(function(){
            alert('失敗');
        });
    </script>
</body>
<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        function aaa(){
            var dfd = $.Deferred();
            dfd.resolve();
            return dfd;
        }
        function bbb(){
            var dfd = $.Deferred();
            dfd.reject();
            return dfd;  //這裡登出後將返回成功,不登出返回失敗
        }
        /*
        這樣寫只有aaa()和bbb()同時完成觸發才彈出,但是任意一個未完成就可以觸發fail()
         */
        //when是針對多個延遲物件的
        //when中的引數必須是延遲物件才起作用
        //如果不是延遲物件就會自動跳過該引數
        $.when(aaa(),bbb()).done(function(){
            alert('成功');
        }).fail(function(){
            alert('失敗');
        });
    </script>
</body>
    $.when(aaa(),bbb(),ccc(),ddd()).done(function(){
            alert('成功');
        });

        aaa()->arguments[0] done()
        bbb()->arguments[1] done()
        ccc()->arguments[2] done()
        ddd()->arguments[3] done()

when是針對多個延遲物件的,比如這裡針對四個延遲物件,when的原始碼中有一個計數器,這裡計數器就是4,觸發一個延遲物件,計數器減1,當計數器為0時。$.when()會返回一個延遲物件deferred.promise(),然後觸發resolve()方法,最後觸發done。

相關閱讀博文:

相關推薦

jQuery原始碼學習()

回撥物件Callbacks 回撥物件Callbacks就是用來管理回撥函式佇列的。 引數說明 它提供幾個便捷的處理引數 once: 確保這個回撥列表只執行一次 memory: 保持以前的值,將新增到這個列表的後面的最新的值立即執行呼叫任何回撥 u

基於MQTT協議的 org.eclipse.paho.client.mqttv3 原始碼學習()

一、主要類介紹 二、重點類程式碼分析 對於長連線,一般是直接從訊息的接收和傳送類開始讀,上面知道paho中訊息傳送和接收是在CommsSender和CommsReceiver實現的, 所以直接差看CommsSender程式碼。 [ja

Vue原始碼學習 ———— Vue原型物件包裝

Vue原型物件的包裝 在Vue官網直接通過 script 標籤匯入的 Vue包是 umd模組的形式。在使用前都通過 new Vue({})。記錄一下 Vue建構函式的包裝。 在 src/core/instance/index.js 這個檔案是 Vue建構函式的出生地。 import { initMixi

jQuery原始碼學習之一 (採用匿名函式自執行模式)

1、如何在jquery官網找到 bug的相關說明?       開啟網址  http://bugs.jquery.com/  後,再搜尋框中輸入bug的 編號(在jquery原始碼的註釋中找到,例如:“ #13335 ”),可找到相關的詳細說明和評論。 2、jquery應用

nginx原始碼學習() 記憶體池結構 ngx_pool_t

1,nginx的記憶體池介紹     為了方便系統模組對記憶體的使用,方便記憶體的管理,nginx自己實現了程序池的機制來進行記憶體的分配和釋放, 首先nginx會在特定的生命週期幫你    統一建立記憶體池,當需要進行記憶體分配的時候統一通過記憶體池中的記憶體進行分配,最

element原始碼學習 —— 簡單元件學習

上一篇部落格中學習了專案的結構,這篇部落格來學幾個簡單的元件的實現。 在上一篇部落格中我們提到了元件的原始碼都是存放在 packages 目錄下的,所以我們從中挑一些元件來學習。先從簡單的入手,來學習 button、radio、checkbox和Inp

vue 原始碼學習 例項初始化和掛載過程

vue 入口 從vue的構建過程可以知道,web環境下,入口檔案在 src/platforms/web/entry-runtime-with-compiler.js(以Runtime + Compiler模式構建,vue直接執行在瀏覽器進行編譯工作) import Vue from './runtime/

vue 原始碼學習() 例項初始化和掛載過程

vue 入口 從vue的構建過程可以知道,web環境下,入口檔案在 src/platforms/web/entry-runtime-with-compiler.js(以Runtime + Compiler模式構建,vue直接執行在瀏覽器進行編譯工作) import Vue from './runtime/

Cordova原始碼學習()-Native回撥JS

Native回撥JS 流程圖 解析 Native Native方法執行完,通過sendPluginResult開始,回撥結果給js sendPluginResult:

jQuery原始碼學習---簡單dom封裝(一)

封裝簡易的dom操作。 一、解決名稱空間和變數汙染 1.作用域 2.立即執行函式 3.閉包 jQuery是一個類陣列物件,裡面有各種方法,當然jQuery的dom選擇器是sizzle很牛叉,據瞭解還有更快的dom選擇器,實力上去再探索。jQuery使用無new建構函式,可以

jquery原始碼學習--jquery api學習

主要是在看《鋒利的jquery》今天終於把第二章 選擇器看完。 感悟:jq確實是個好庫,非常好實用,裡面的api函式有很多,因此也預示著它有很多很方便的功能, 所以在看原始碼之前,必須要學習jquery的使用。 在第二章中,關於後代選擇器和過濾選擇器這裡做一個總結(見《鋒利

jQuery原始碼學習(四)

佇列queue() 佇列(先進先出)方法,執行順序的管理。 <script type="text/javascript"> //大體框架 //佇列其實就是一個數組 jQuery.extend([//工具方法

lua_gc 原始碼學習

普及下常識:GC 是 garbage collector 資源回收器; 初期的 Lua GC 採取的是 stop the world 的實現。一旦產生 gc 就需要期待全部 gc 流程走完。lua 自己是個很精簡的體系,但不代表處理的資料量也必然很小。 從 Lua 5.1

Mybatis 原始碼學習() Mapper 介面和sql的對映

問題:xml中的sql語句是怎麼被對映到Mapper介面的一個方法上的?弄明白了mapper是如何註冊的了以後,發現xml檔案中的namespace是關鍵。實際還是去找那個java介面檔案。那麼找到了介面檔案,註冊了mapper那這個mapper又是怎麼反過來找到xml中配置

深入學習jquery原始碼之擴充套件jquery次開發

深入學習jquery原始碼之jquery二次開發 jquery.js的設計與實現 (function (global, factory) { if (typeof module === "object" && typeof module.exports === "o

[jQuery學習系列 ]2-JQuery學習-數組操作

hit fun 沒有 hello second tag you jquer 操作 前言 上一篇內容 已經對於Jquery 有了一些認識, 包括Jquery的選擇器和DOM對象, 那麽這一篇繼續來看下Jquery中很實用的Jquery對於數組的操作. Jquery中對數組的

[jQuery學習系列三 ]3-JQuery學習-字典操作

huang math 喜歡 lec 十分 地址 red gets dao 前言:如果看過了第一篇和第二篇, 相信大家會對jQuery有個初步的認識了, 對於jQuery的選擇器和數組的操作都已經很熟悉了, 這一篇就單獨羅列jQuery中字典的操作相關的內容. 1. 數組中

前端學習(十)jquery屬性(筆記)

after box jquery屬性 remove ren pen nbsp mov 操作 jq裏面操作屬性的方法: 設置屬性: 設置一個: $(this).attr(‘src‘,‘img/pic2.jpg‘);

Vue原始碼學習)——生命週期

官網對生命週期給出了一個比較完成的流程圖,如下所示: 從圖中我們可以看到我們的Vue建立的過程要經過以下的鉤子函式: beforeCreate =&gt; created =&gt; beforeMount =&gt; mounted =&gt; beforeUpda

spring原始碼學習之路---IOC實現原理(

上一章我們已經初步認識了BeanFactory和BeanDefinition,一個是IOC的核心工廠介面,一個是IOC的bean定義介面,上章提到說我們無法讓BeanFactory持有一個Map package org.springframework.beans.factory.supp