JQuery中的回调对象

JQuery中的回调对象

回调对象(Callbacks object)模块是JQuery中的一个很基础的模块,不少其余的模块(好比Deferred、Ajax等)都依赖于Callbacks模块的实现。
本文主要描述回调对象的基本功能、具体实现,以及使用到的设计思想。jquery

1. 基本功能

从jquery的API 中能够了解到,Callbacks对象是JQuery自1.7之后提供的一个管理回调函数的工具,提供基本的增长、删除、调用和禁用回调函数的能力。git

1.1 基本配置

Callbacks提供以下四个配置供开发者使用:github

  • once 保证全部回调函数只能执行一次。
  • memory 记录上一次调用回调函数的参数值。若是在调用回调函数以后又经过add新回调函数,那么会当即使用memory记录的值调用新增的回调函数。
  • unique 保证每一个回调函数只能被添加一次,即回调函数列表中每一个函数都惟一。
  • stopOnFalse 若是一个回调函数返回false,则终止调用其余剩余会回调函数。

开发者能够经过以上配置的字符串组合(以空格分隔)初始化回调对象。设计模式

var callbacks = $.Callbacks('unique memory');  //回调函数列表中每一个函数必须惟一,且要记录上一次调用的参数值

1.2 基本API

Callbacks提供以下API:api

  1. callbacks.add()
    向回调函数列表中增长一个回调函数或回调函数集合。
  2. callbacks.remove()
    从回调函数列表中删除一个回调函数或回调函数的集合。
  3. callbacks.disable()
    禁用改回调函数列表,不在执行任何动做。
  4. callbacks.disabled()
    回调函数列表是否被禁用。
  5. callbacks.empty()
    清空回调函数列表。
  6. callbacks.has()
    若是带有参数,则表示该参数是否在回调函数列表中,不然表示回调函数列表是否为空。
  7. callback.fire()
    指定参数调用列表中全部的回调函数。
  8. callback.fireWith()
    指定上下文和参数调用列表中全部的回调函数。
  9. callback.fired()
    回调函数是否被执行过。
  10. callback.lock()
    锁定回调函数列表,使其保持当前的状态不变。
  11. callback.locked()
    回调函数列表是否被锁定。

2. 具体实现

2.1 add和fire/fireWith

从上面的API中已经能够基本看出Callbacks的基本实现方式了,即维护一个回调函数列表(list)和一个参数队列(queue)。每次经过add向list中增长回调函数,而后经过fire/fireWith从queue中取出参数按序执行回调函数。
JQuery的Callbacks模块代码很简单,其核心就是一个内部的fire函数(非api中fire函数,请看下文fire介绍):数组

// Actual callback list
    list = [],
    // Stack of fire calls for repeatable lists
    stack = !options.once && [],
    // Fire callbacks
    fire = function( data ) {
        memory = options.memory && data;
        fired = true;
        firingIndex = firingStart || 0;
        firingStart = 0;
        firingLength = list.length;
        firing = true;
        for ( ; list && firingIndex < firingLength; firingIndex++ ) {
            if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
                memory = false; // To prevent further calls using add
                break;
            }
        }
        firing = false;
        if ( list ) {
            if ( stack ) {
                if ( stack.length ) {
                    fire( stack.shift() );
                }
            } else if ( memory ) {
                list = [];
            } else {
                self.disable();
            }
        }
    }

以上代码摘自JQuery-1.12版本,代码中的list就是回调函数列表,stack就是参数队列queue。能够看出,每次执行fire函数都会遍历list,用传进来的参数调用每一个回调函数,若是参数队列不为空,则递归的执行,一直到参数队列取空为止。app

咱们再看add函数的代码:函数

add: function() {
        if ( list ) {
            // First, we save the current length
            var start = list.length;
            (function add( args ) {
                jQuery.each( args, function( _, arg ) {
                    var type = jQuery.type( arg );
                    if ( type === "function" ) {
                        if ( !options.unique || !self.has( arg ) ) {
                            list.push( arg );
                        }
                    } else if ( arg && arg.length && type !== "string" ) {
                        // Inspect recursively
                        add( arg );
                    }
                });
            })( arguments );
            // Do we need to add the callbacks to the
            // current firing batch?
            if ( firing ) {
                firingLength = list.length;
            // With memory, if we're not firing then
            // we should call right away
            } else if ( memory ) {
                firingStart = start;
                fire( memory );
            }
        }
        return this;
    }

add函数会根据参数类型将参数添加到回调函数列表中,即若是参数是一个函数(且要求unique),那么就直接放进回调函数列表中,若是参数是一个类数组,那么就递归调用add函数,将参数中的全部回调函数放进对调函数列表中。
请注意,若是在初始化Callbacks对象时设置了memory,那么在调用add函数的时候,会使用memory记住的参数值调用每个新加的回调函数。工具

再看fire和fireWith函数:this

// 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;
    },

这两个函数将参数放进参数队列中,最终都调用内部fire函数将参数传递给回调函数。

2.2 disable和lock

此处要特别说明一下disable和lock的区别,先看这两个函数的实现:

// Have the list do nothing anymore
    disable: function() {
        list = stack = memory = undefined;
        return this;
    },
    
    // Lock the list in its current state
    lock: function() {
        stack = undefined;
        if ( !memory ) {
            self.disable();
        }
        return this;
    },

这两个函数都禁用了回调函数队列,即不能再经过fire/fireWith调用回调函数。可是,若是咱们在初始化Callbacks对象的时候,设置了memory的话,那么当回调函数被lock以后,经过add新增的回调函数依然可使用memory的值调用。

3. 总结

不难看出,整个Callbacks的设计思想就是基于发布订阅(Pub/Sub)的观察者模式。与通常实现方式相比较而言,一个对象再也不直接调用另外一个对象的方法,而是观察另外一个对象的某个特定的任务或活动,这个观察的对象就叫订阅者(Subscriber),被观察的对象就叫发布者(Publisher),当发布者的任务或活动完成时,会通知订阅者。 这种设计模式的主要目的就是为了达到程序之间的松耦合。

相关文章
相关标签/搜索