成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

jQuery 源碼系列(七)Callbacks 函數(shù)

timger / 3156人閱讀

摘要:的支持的方法有幾個(gè)主要的,和,比如官方有一個(gè)例子這兩個(gè)作為函數(shù)調(diào)用的生成從基本可以看出,函數(shù)生成了一個(gè)對(duì)象,這個(gè)對(duì)象的方法是添加回調(diào)函數(shù),而方法則是執(zhí)行回調(diào)函數(shù)。

歡迎來(lái)我的專(zhuān)欄查看系列文章。

講真,Sizzle 的源碼真的太壓抑了,以至于寫(xiě) Sizzle 文章的這段時(shí)間里都非常的痛苦,剛開(kāi)始覺(jué)得它還挺有意思的,越到后面越覺(jué)得代碼很難讀懂,煩。

寒假也過(guò)完了,在家里待了兩周的時(shí)間,感覺(jué)不錯(cuò),這期間學(xué)習(xí)的事情都拋在腦后,學(xué)得非常少,把 cctv 的《中國(guó)通史》系列節(jié)目給看完了,對(duì)于歷史迷的我來(lái)說(shuō),也算是一種心安吧。

今天的主題不在時(shí) Sizzle,停頓了兩周,感覺(jué)清醒了很多,之前被 Sizzle 支配的痛苦已經(jīng)消去大半,今天來(lái)介紹一下 jQuery 的 Callbacks 函數(shù)。

Callbacks 的使用

jQuery 內(nèi)部提供了很多基礎(chǔ)功能的方法,比如 $.ajax()、$.each() 和 $.Callbacks(),這些方法既可以在內(nèi)部進(jìn)行使用,又可以被開(kāi)發(fā)者拿到外部多帶帶使用。

Callbacks 的支持的方法有幾個(gè)主要的,add、fire、remove 和 disable,比如官方有一個(gè)例子:

// 這兩個(gè)作為 callback 函數(shù)
function fn1( value ) {
  console.log( value );
}
 
function fn2( value ) {
  fn1("fn2 says: " + value);
  return false;
}

// 調(diào)用 jQuery 的 Callbacks 生成 callbacks
var callbacks = $.Callbacks();
callbacks.add( fn1 );

callbacks.fire( "foo!" );
// "foo!"
 
callbacks.add( fn2 );
 
callbacks.fire( "bar!" );
// "bar!"
// "fn2 says: bar!"

從基本 demo 可以看出,$.Callbacks() 函數(shù)生成了一個(gè) callbacks 對(duì)象,這個(gè)對(duì)象的 .add() 方法是添加回調(diào)函數(shù),而 .fire() 方法則是執(zhí)行回調(diào)函數(shù)。

.remove() 方法是移除回調(diào)函數(shù):

var callbacks = $.Callbacks();
callbacks.add( fn1 );

callbacks.fire( "foo!" );
// "foo!"
 
callbacks.add( fn2 );

callbacks.fire( "bar!" );
// "bar!"
// "fn2 says: bar!"

callbacks.remove( fn2 );

callbacks.fire( "foobar" );
// "foobar"

$.Callbacks() 還支持幾個(gè)參數(shù),表示執(zhí)行回調(diào)的幾種效果,$.Callbacks("once")

once: 確保這個(gè)回調(diào)列表只執(zhí)行 .fire() 一次(像一個(gè)遞延 Deferred)

memory: 保持以前的值,將添加到這個(gè)列表的后面的最新的值立即執(zhí)行調(diào)用任何回調(diào) (像一個(gè)遞延 Deferred)

unique: 確保一次只能添加一個(gè)回調(diào)(所以在列表中沒(méi)有重復(fù)的回調(diào))

stopOnFalse: 當(dāng)一個(gè)回調(diào)返回false 時(shí)中斷調(diào)用

此方法還支持多個(gè)參數(shù),比如$.Callbacks("once memory"),具體的使用請(qǐng)參考這個(gè)鏈接。

Callbacks 的源碼

在放 jQuery 3.0 的源碼之前,我們先來(lái)簡(jiǎn)單的模擬一下 Callbacks 函數(shù),來(lái)實(shí)現(xiàn)其基本的功能:

var Callbacks = function(){
  var Cb = {
    callbacks: [],
    add: function(fn){
      this.callbacks.push(fn);
      return this;
    },
    fire: function(value){
      this.callbacks.forEach(function(fn){
        fn(value);
      });
      return this;
    }  
  }
  return Cb;
}

// 測(cè)試
var callbacks = Callbacks();
callbacks.add(fn1);
callbacks.fire("test"); //"test"

可以看到其實(shí)一個(gè)簡(jiǎn)單的 Callbacks 函數(shù)實(shí)現(xiàn)起來(lái)還是非常簡(jiǎn)單的。

整個(gè)的 Callbacks 源碼其實(shí)大致如下:

jQuery.Callbacks = function(options){
  // 先對(duì)參數(shù)進(jìn)行處理,比如 once、unique 等
  options = createOptions(options);

  // 參數(shù)定義,包括一些 flag 和 callbacks 數(shù)組
  var list = [], queue = [] ...

  // fire 是遍歷數(shù)組,回掉函數(shù)的執(zhí)行
  var fire = function(){
    ...
  }

  // self 是最終返回的對(duì)象
  var self = {
    add: function(){...},
    remove: function(){...},
    has: function(){...},
    disable: function(){...},
    fireWith: function(){...},//這個(gè)其實(shí)是 fire 函數(shù)的執(zhí)行
    fire: function(){...}
    ...
  }
  return self;
}

因?yàn)榍懊嬉呀?jīng)簡(jiǎn)單的介紹過(guò)了如何實(shí)現(xiàn)一個(gè)基本的 Callbacks 函數(shù),這里稍微清晰了一點(diǎn),來(lái)看下 createOptions 函數(shù),這個(gè)函數(shù)主要是對(duì)類(lèi)似于 $.Callbacks("once memory")類(lèi)型對(duì) callback 進(jìn)行 flag 分離:

function createOptions(options) {
  var object = {};
  jQuery.each(options.match(rnothtmlwhite) || [], function (_, flag) {
    object[flag] = true;
  });
  return object;
}

其中 rnothtmlwhite 是一個(gè)正則表達(dá)式 /[^x20 f]+/g,用來(lái)獲得所有的 flag 標(biāo)志。createOptions 的結(jié)果是一個(gè)對(duì)象,鍵值分別是 flag 和 boolean。

那么現(xiàn)在的主要的問(wèn)題,就全在那些 flag 上面來(lái),"once memory unique stopOnFalse"

源碼奉上:

jQuery.Callbacks = function(options) {
  // flag 處理
  options = typeof options === "string" ? createOptions(options) : jQuery.extend({}, options);

  var // Flag to know if list is currently firing
  firing,
    // Last fire value for non-forgettable lists
    memory,
    // Flag to know if list was already fired
    fired,
    // Flag to prevent firing
    locked,
    // Actual callback list
    list = [],
    // Queue of execution data for repeatable lists
    queue = [],
    // Index of currently firing callback (modified by add/remove as needed)
    firingIndex = -1,
    // Fire callbacks
    fire = function() {
      // 只執(zhí)行一次,以后都不執(zhí)行了
      locked = locked || options.once;

      // Execute callbacks for all pending executions,
      // respecting firingIndex overrides and runtime changes
      fired = firing = true;
      for (; queue.length; firingIndex = -1) {
        memory = queue.shift();
        while (++firingIndex < list.length) {
          // 回調(diào)執(zhí)行函數(shù),并檢查是否 stopOnFalse,并阻止繼續(xù)運(yùn)行
          if (list[firingIndex].apply(memory[0], memory[1]) === false && options.stopOnFalse) {
            // Jump to end and forget the data so .add doesn"t re-fire
            firingIndex = list.length;
            memory = false;
          }
        }
      }

      // Forget the data if we"re done with it
      if (!options.memory) {
        memory = false;
      }

      firing = false;

      // locked 在這里實(shí)現(xiàn)
      if (locked) {
        // 雖然鎖住但是是 memory,保留 list 以后使用
        if (memory) {
          list = [];
          // 拜拜...
        } else {
          list = "";
        }
      }
    },
    // Actual Callbacks object
    self = {
      // Add a callback or a collection of callbacks to the list
      add: function() {
        if (list) {
          // If we have memory from a past run, we should fire after adding
          if (memory && !firing) {
            firingIndex = list.length - 1;
            queue.push(memory);
          }

          (function add(args) {
            jQuery.each(args, function(_, arg) {
              if (jQuery.isFunction(arg)) {
                if (!options.unique || !self.has(arg)) {
                  list.push(arg);
                }
              } else if (arg && arg.length && jQuery.type(arg) !== "string") {
                // Inspect recursively
                add(arg);
              }
            });
          })(arguments);

          if (memory && !firing) {
            fire();
          }
        }
        return this;
      },
      // Remove a callback from the list
      remove: function() {
        jQuery.each(arguments, function(_, arg) {
          var index;
          while ((index = jQuery.inArray(arg, list, index)) > -1) {
            list.splice(index, 1);

            // Handle firing indexes
            if (index <= firingIndex) {
              firingIndex--;
            }
          }
        });
        return this;
      },
      // Check if a given callback is in the list.
      // If no argument is given, return whether or not list has callbacks attached.
      has: function(fn) {
        return fn ? jQuery.inArray(fn, list) > -1 : list.length > 0;
      },
      // Remove all callbacks from the list
      empty: function() {
        if (list) {
          list = [];
        }
        return this;
      },
      // Disable .fire and .add
      // Abort any current/pending executions
      // Clear all callbacks and values
      disable: function() {
        locked = queue = [];
        list = memory = "";
        return this;
      },
      disabled: function() {
        return !list;
      },
      // Disable .fire
      // Also disable .add unless we have memory (since it would have no effect)
      // Abort any pending executions
      lock: function() {
        locked = queue = [];
        if (!memory && !firing) {
          list = memory = "";
        }
        return this;
      },
      locked: function() {
        return !!locked;
      },
      // Call all callbacks with the given context and arguments
      fireWith: function(context, args) {
        if (!locked) {
          args = args || [];
          args = [context, args.slice ? args.slice() : args];
          queue.push(args);
          if (!firing) {
            fire();
          }
        }
        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;
};

總的來(lái)說(shuō),這種 pub/sub 模式的代碼還是比較容易看懂的,有些疑問(wèn)的地方,比如源碼中其實(shí)有兩個(gè)數(shù)組,list 是隊(duì)列數(shù)組,本應(yīng)該叫做 queue,但是 queue 數(shù)組已經(jīng)被定義,且 queue 的作用是用來(lái)存儲(chǔ) fire 執(zhí)行時(shí)的參數(shù),這點(diǎn)不能搞混。

還有就是當(dāng)整個(gè)代碼 firing 這個(gè)參數(shù),導(dǎo)致當(dāng)函數(shù)正在運(yùn)行的時(shí)候,即執(zhí)行兩次 fire 的時(shí)候,需要補(bǔ)充 queue 元素,但 fire() 函數(shù)只執(zhí)行一次。

總結(jié)

jQuery.Callbacks 沿用 jQuery 一貫的套路,最后 return self,剛看第一遍第二遍的時(shí)候,有點(diǎn)模模糊糊的,主要還是 once、memory 等 flag 參數(shù)干擾我的視線,尤其是其這些 flag 標(biāo)志的實(shí)現(xiàn),難受。

參考

jQuery.Callbacks 中文文檔

jQuery 2.0.3 源碼分析 回調(diào)對(duì)象 - Callbacks

本文在 github 上的源碼地址,歡迎來(lái) star。

歡迎來(lái)我的博客交流。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/81650.html

相關(guān)文章

  • jQuery源碼解析之$.queue()、$.dequeue()和jQuery.Callbacks(

    摘要:作為此時(shí)不存在,直接從數(shù)據(jù)緩存中獲取并返回。作用是觸發(fā)中的回調(diào)函數(shù),的表示只讓觸發(fā)一次后,就需要清理,表示是將清空成空數(shù)組還是空字符。 showImg(https://segmentfault.com/img/remote/1460000019558449); 前言:queue()方法和dequeue()方法是為 jQuery 的動(dòng)畫(huà)服務(wù)的,目的是為了允許一系列動(dòng)畫(huà)函數(shù)被異步調(diào)用,但不...

    itvincent 評(píng)論0 收藏0
  • jQuery源碼學(xué)習(xí)之Callbacks

    摘要:源碼學(xué)習(xí)之的通過(guò)回調(diào)實(shí)現(xiàn)異步,其實(shí)現(xiàn)核心是。回調(diào)函數(shù)隊(duì)列中的函數(shù)返回時(shí)停止觸發(fā)回調(diào)函數(shù)隊(duì)列只能被觸發(fā)一次記錄上一次觸發(fā)隊(duì)列傳入的值,新添加到隊(duì)列中的函數(shù)使用記錄值作為參數(shù),并立即執(zhí)行。實(shí)際是,內(nèi)部則調(diào)用了在定義的局部函數(shù)。 jQuery源碼學(xué)習(xí)之Callbacks jQuery的ajax、deferred通過(guò)回調(diào)實(shí)現(xiàn)異步,其實(shí)現(xiàn)核心是Callbacks。 使用方法 使用首先要先新建一個(gè)...

    lmxdawn 評(píng)論0 收藏0
  • jQuery 源碼系列(十九)ajax 的相關(guān)操作

    摘要:歡迎來(lái)我的專(zhuān)欄查看系列文章。主要來(lái)看函數(shù)函數(shù)返回值有兩個(gè),其中就是發(fā)送函數(shù)了,一步一步,發(fā)送下來(lái),無(wú)需多說(shuō)明。參考源碼分析系列整體結(jié)構(gòu)源碼分析系列總結(jié)觸碰異步詳解本文在上的源碼地址,歡迎來(lái)。 歡迎來(lái)我的專(zhuān)欄查看系列文章。 關(guān)于 ajax,東西太多了,我本來(lái)想避開(kāi) ajax,避而不提,但覺(jué)得 ajax 這么多內(nèi)容,不說(shuō)又少點(diǎn)什么,就簡(jiǎn)單點(diǎn)來(lái)介紹吧,加上最近準(zhǔn)備內(nèi)推面試的時(shí)候,看了不少 a...

    Dean 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<