jQuery源码阅读(十二)---Callbacks回调对象

还记得jQuery源码阅读(一)的时候,整理了jQuery库的整体架构,主要分为三个模块:
入口模块、底层支持模块和功能模块,各个模块之间也是有关联的。
前面几篇博文分别分析了jQuery库的入口模块,最主要的是init方法的分析;还分析了jQuery底层支持模块中的工具方法,主要是通过jQuery.extend()方法来扩展jQuery静态方法的。底层支持模块中,出了工具方法之外,还有很多模块,个人觉得这些模块的一个作用就是增强代码的复用性和易用性。比如回调对象模块,异步队列模块,延迟对象,队列,数据缓存,浏览器支持模块,Sizzle选择器模块等等。这次主要先整理jQuery中的回调对象。

源码框架及使用

jQuery.Callbacks = function(flags){
    add = function(){
    }
    fire = function(){
    }
    self = {
        //定义了一些变量
        //下面是用来管理函数的方法
        add: function(){
        },
        fire: function(){
        },
        fireWith: function(){
        },
        has: function(){
        },
        fired: function(){
        },
        lock: function(){
        },
        locked: function(){
        }
        //等等
    }
    return self;
}

由上面架构可以知道,jQuery.Callbacks函数主要是返回一个回调对象,这个回调对象通过一些方法来管理回调函数。具体什么方法呢?有add(),fire(),fireWith(),lock()等等很多方法,主要是通过将函数加入到数组列表中的方式,然后一个个去执行从而达到管理函数的目的。
可以看到,Callbacks方法里面,定义了两个私有函数,为什么要这么做呢?
我的理解是:为了减少内存的占用。因为Callbacks函数最终是要返回一个回调对象的,而回调对象中有管理函数的一些方法,如果把这些方法直接写到回调对象中,也是可以的,但是这就意味着每个回调对象都会有一份自己的方法,只是这些方法都是同名的,这就导致内存的浪费。而把add和fire定义成两个私有的变量,不管调用多少次jQuery.Callbacks方法,返回的回调对象中的方法都会去调Callbacks函数中的私有方法,并且指向的是相同的内存地址。

下来我们看下jQuery.Callbacks的参数情况:

1. once               //只执行一遍函数,不会重复执行
2. memory             //会将所有add进函数列表的函数执行
3. unique             //不允许列表中有相同的函数
4. stopOnFalse        //对于返回值为false的函数,执行之后停止对后面函数的执行

首先来体会下这几个参数的作用:

function a1(){
    console.log('111');
    return false;
}
function a2(){
    console.log('222');
}

分别展示四组图来显示Callbacks中四个参数的意义:

  • once标志

这里写图片描述

这里写图片描述

  • memory标志

这里写图片描述

这里写图片描述

  • unique标志

这里写图片描述

这里写图片描述

  • stopOnFalse标志

这里写图片描述

这里写图片描述

从上面四组图可以很容易理解四个参数的含义,那么这四个参数具体在源码中是如何影响的?我想用下面的图来说明
这里写图片描述

源码分析

根据上面那个图,我们就大致应该知道:
add函数中,将函数fn Push到list数组里,同时会判断unique和memory两个参数,如果unique参数为真,那么不会将相同的函数都push到数组里;如果memory为真,会add之后再调一次fire函数。

fire函数中,将数组中所有的函数进行执行,同时会判断once和stopOnFalse两个参数。如果once为真,只会将所有函数执行一遍;如果stopOnFalse为真,那么对于返回值为false的函数,执行之后不会再对后面的函数执行。

基于上面两块思路来看源码,会更好理解一些。

add方法

回调对象中的add方法:

add: function() {
    if ( list ) {
        var length = list.length;
        //这里的add方法是Callbacks函数中的私有方法
        add( arguments );

        if ( firing ) {
            firingLength = list.length;
        } else if ( memory && memory !== true ) {
            //判断memory参数,并且调fire函数
            firingStart = length;
            fire( memory[ 0 ], memory[ 1 ] );
        }
    }
    return this;
}

Callbacks函数中的私有add方法:

add = function( args ) {
    var i,
    length,
    elem,
    type,
    actual;
    for ( i = 0, length = args.length; i < length; i++ ) {
        elem = args[ i ];
        type = jQuery.type( elem );
        if ( type === "array" ) {
            //如果参数为数组,再递归调add方法
            add( elem );
        } else if ( type === "function" ) {
            //判断unique标志,并且看list数组中是否有该函数
            if ( !flags.unique || !self.has( elem ) ) {
                list.push( elem );
            }
        }
    }
}

fire方法

self对象中的fire函数:

fire: function() {
    //fire方法去调self对象的fireWith方法,该方法不仅将当前作用域传了进去,还将fire中的参数也传进去了
    self.fireWith( this, arguments );
    return this;
}
fireWith: function( context, args ) {
    if ( stack ) { //一开始stack为空数组,转换成Boolean值也是真;设置了once,会将stack置为undefined,所以重复的fire都不会真正执行
        if ( firing ) {  //firing这个标志是解决嵌套调用fire的情况;与stack结合起来处理,stack用于保存上下文和参数
            if ( !flags.once ) {  
                stack.push( [ context, args ] );
            }
        } else if ( !( flags.once && memory ) ) {  //第一次执行或者未设置once参数多次执行
            fire( context, args );
        }
    }
    return this;
}
fire = function( context, args ) {
    args = args || [];
    //如果未设置memory参数,那么memory变量为true;
    memory = !flags.memory || [ context, args ];
    fired = true;
    firing = true;
    firingIndex = firingStart || 0;
    firingStart = 0;
    firingLength = list.length;
    for ( ; list && firingIndex < firingLength; firingIndex++ ) {
        //对list中每一个函数执行,如果返回false并且stopOnFalse参数为真,那么跳出循环,不再对后面的函数执行。
        if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
            memory = true; // Mark as halted
            break;
        }
    }
    firing = false;
    if ( list ) {
        if ( !flags.once ) {  
            if ( stack && stack.length ) {
                memory = stack.shift();
                self.fireWith( memory[ 0 ], memory[ 1 ] );
            }
        } else if ( memory === true ) {
            self.disable();        //这个用于将stack标记设为undefined
        } else {
            list = [];
        }
    }
}

对于Callbacks回调模块,主要就是这两个函数,当然,还有像self.hasself.empty, self.lock, self.disable等方法,不过相比来说,这几个方法都比较好理解。这里大概提下lock和disable方法,lock方法是禁止后面的fire函数; 而disable方法会禁止后面要执行的所有操作。总体上来说,Callbacks模块,如果能考虑到所有的情况,并可以沿着源码走一遍,那么对于整个模块已经理解得差不多了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值