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

来源:互联网 发布:换域名 编辑:程序博客网 时间:2024/05/17 07:25

还记得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模块,如果能考虑到所有的情况,并可以沿着源码走一遍,那么对于整个模块已经理解得差不多了。

原创粉丝点击