• JQuery中的回调对象


    JQuery中的回调对象

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

    1. 基本功能

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

    1.1 基本配置

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

    • once 保证所有回调函数只能执行一次。
    • memory 记录上一次调用回调函数的参数值。如果在调用回调函数之后又通过add新回调函数,那么会立即使用memory记录的值调用新增的回调函数。
    • unique 保证每个回调函数只能被添加一次,即回调函数列表中每个函数都唯一。
    • stopOnFalse 如果一个回调函数返回false,则终止调用其他剩余会回调函数。

    开发者可以通过以上配置的字符串组合(以空格分隔)初始化回调对象。

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

    1.2 基本API

    Callbacks提供如下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,用传进来的参数调用每个回调函数,如果参数队列不为空,则递归的执行,一直到参数队列取空为止。

    我们再看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函数:

        // 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),当发布者的任务或活动完成时,会通知订阅者。
    这种设计模式的主要目的就是为了达到程序之间的松耦合。

  • 相关阅读:
    建立一个简单的通讯录
    建立一个图书管理系统(oc)
    OC 学习第六天(敲代码时遇到的一些错误)
    Serv-U FTP Server 15.1.2学习笔记
    Myeclipse&Tomcat中遇到的部分问题---其一
    MySQL存储过程和函数(三)
    MySQL存储过程和函数(二)
    MySQL存储过程和函数(一)
    mybatis拦截器分页
    Java--最大子序列和实现
  • 原文地址:https://www.cnblogs.com/bingooo/p/4965998.html
Copyright © 2020-2023  润新知