博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jQuery.Callbacks 源码解读二
阅读量:5046 次
发布时间:2019-06-12

本文共 10012 字,大约阅读时间需要 33 分钟。

一、参数标记

/* * once: 确保回调列表仅只fire一次 * unique: 在执行add操作中,确保回调列表中不存在重复的回调 * stopOnFalse: 当执行回调返回值为false,则终止回调队列的执行 * momery: 记录上一次fire时的参数,并在add中传递给fire和执行fire,执行时firingIndex为上一次fire时的firingLength */

二、源码解读分析

var optionsCache = {},    // Used for splitting on whitespace    core_rnotwhite = /\S+/g;// Convert String-formatted options into Object-formatted ones and store in cachefunction createOptions( options ) {    // 多个变量指向同一对象(或数组)引用时,其中一个变量修改了被引用对象的内部结构,其他引用变量也会表现出来    var object = optionsCache[ options ] = {};    jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {        object[ flag ] = true; // optionsCache[ options ][ flag ] = true;    });    return object;}jQuery.Callbacks = function( options ) {    // Convert options from String-formatted to Object-formatted if needed    // (we check in cache first)    options = typeof options === "string" ?        // 缓存所有的参数标志,当再次传递已传递过的参数标志,则使用缓存值optionsCache[ options ]                ( optionsCache[ options ] || createOptions( options ) ) :        // 说明也可以这样$.Callbacks({once:true, memory:true})使用        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,        // End of the loop when firing        firingLength,        // Index of currently firing callback (modified by remove if needed)        firingIndex,        // First callback to fire (used internally by add and fireWith)        firingStart,        // Actual callback list        list = [],        // Stack of fire calls for repeatable lists        stack = !options.once && [],        // Fire callbacks        // data为fireWith内部整理的args数组        fire = function( data ) {            memory = options.memory && data;            fired = true;            // 处理在add中,options.memory = true;的情况            firingIndex = firingStart || 0;            firingStart = 0;            firingLength = list.length;            firing = true;            for ( ; list && firingIndex < firingLength; firingIndex++ ) {                // 正在执行的回调返回值为false 且 options.stopOnFalse为true,则终止回调队列的执行                if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {                    memory = false; // To prevent further calls using add                    break;                }            }            firing = false;            if ( list ) {                // 处理正在执行的回调中执行fireWith的操作;                if ( stack ) {                    if ( stack.length ) {                        fire( stack.shift() );                    }                }                // 上一分支状态为回调执行过,且可以执行多次                // 此时 options.once = true; 这里将list设置为[],只是确保下次执行fire时,无回调执行                // 但是如果 options.memory = true; 仍然会执行add中的fire操作,因为此时回调列表中已有回调                else if ( memory ) {                    list = [];                }                else {                    self.disable();                }            }        },        // Actual Callbacks object        self = {            // Add a callback or a collection of callbacks to the list            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" ) {                                // 回调不唯一 或 唯一且不存在,则push                                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?                    // 正在执行的回调执行了add操作,则更新firingLength                    if ( firing ) {                        firingLength = list.length;                    // With memory, if we're not firing then                    // we should call right away                    // 如果options.memory为true,则再次执行fire,且参数相同,fire中的firingIndex为此时的firingStart                    }                    else if ( memory ) {                        firingStart = start;                        fire( memory );                    }                }                return this;            },            // Remove a callback from the list            remove: function() {                if ( list ) {                    jQuery.each( arguments, function( _, arg ) {                        var index;                                        while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {                            // 查找到所对应的索引,则移除索引项                            list.splice( index, 1 );                            // Handle firing indexes                            // 正在执行的回调执行了remvoe操作,则更新firingLength和firingIndex的值                            if ( firing ) {                                if ( index <= firingLength ) {                                    firingLength--;                                }                                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 && list.length );            },            // Remove all callbacks from the list            empty: function() {                list = [];                return this;            },            // Have the list do nothing anymore            // 禁用add,remove,fire主要方法的工作            disable: function() {                list = stack = memory = undefined;                return this;            },            // Is it disabled?            disabled: function() {                return !list;            },            // Lock the list in its current state            lock: function() {                // 如果回调执行过,则将阻止self.fire操作                // 但如果 options.memory = true,则仍然会执行fire操作                stack = undefined;                                // 回调未执行,则禁用                if ( !memory ) {                    self.disable();                }                return this;            },            // Is it locked?            locked: function() {                return !stack;            },            // Call all callbacks with the given context and arguments            fireWith: function( context, args ) {                args = args || [];                args = [ context, args.slice ? args.slice() : args ];                                // 回调未执行 或 已执行且可以执行多次                if ( list && ( !fired || stack ) ) {                    // 正在执行的回调函数执行了fireWith操作( 暗指回调列表已执行过,且可以执行多次,stack = []; )                    // 该函数需要条件执行,或有移除该函数的操作,否则陷入死循环,详见例2                    if ( firing ) {                        stack.push( args );                    }                    // 正在执行的回调函数没有执行fireWith操作                    else {                        fire( args );                    }                }                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;};

三、示例

例1:

ContractedBlock.gif
ExpandedBlockStart.gif
$(function(){    // 定义三个将要增加到回调列表的回调函数fn1,fn2,fn3            function fn1(arg){        console.log( 'fn1 says:' + arg );        // 在fn1中执行Callbacks.add操作,此时Callbacks函数内部的firingLength将会得到更新        $callbacks.add(fn2);    }    function fn2(arg){        console.log( 'fn2 says:' + arg );    }    function fn3(arg){        console.log( 'fn3 says:' + arg );    }        // Callbacks传递了memory    // 也可以这样使用$.Callbacks({ memory: true });    var $callbacks = $.Callbacks('memory');        // 将fn1增加到回调列表中,因为在fn1中有执行了add(fn2)操作,因此回调列表中的回调为fn1,fn2    $callbacks.add(fn1);        // output: fn1 says:foo    // output: fn2 says:foo    $callbacks.fire('foo');        // 将之前fire的参数传递给最近增加的回调fn3,并执行fn3    // output: fn3 says:foo    $callbacks.add(fn3);        // 再执行一次fire,注意此时回调列表中的回调一次是fn1,fn2,fn3,fn2    // output: fn1 says:baz    // output: fn2 says:baz    // output: fn3 says:baz    // output: fn2 says:baz    // 如果期望回调列表中只有fn1,fn2,fn3,只需在Callbacks函数中传入unique    $callbacks.fire('baz');});
View Code

例2

ContractedBlock.gif
ExpandedBlockStart.gif
$(function(){        function fn1(arg){        console.log( 'fn1 says:' + arg );    }    function fn2(arg){        console.log( 'fn2 says:' + arg );        $callbacks.fireWith(window, ['yjh']);                // 一定要执行这一步,否则将会陷入死循环        $callbacks.remove(fn2);    }        var $callbacks = $.Callbacks();    $callbacks.add(fn1);        // output: fn1 says:foo    $callbacks.fire('foo');        $callbacks.add(fn2);        // output: fn1 says:baz    // output: fn2 says:baz    // output: fn1 says:yjh    $callbacks.fire('baz');});
View Code

PS:

此前写过一篇关于jQuery.Callbacks源码分析的随笔,理解不透彻,今天又重新翻阅了一下,记录一下自己的源码阅读,相比之前,感觉好多了。

阅读前,可以先看API,弄清楚四个参数标志,'once', 'memory', 'unique', 'stopOnFalse', 简单的执行add, fire操作,然后再看源码;

阅读顺序:

1、先阅读var声明的变量,fire函数的前半部分,self对象中的add, remove函数,有些难以理解暂时往下看;

2、然后阅读self对象中的fire,fireWith,最后再来阅读fire函数,弄清楚后再看其他self对象中的方法。

转载请注明出处:

转载于:https://www.cnblogs.com/yangjunhua/p/3381258.html

你可能感兴趣的文章
[数字图像处理]常见噪声的分类与Matlab实现
查看>>
开发指南专题六:JEECG微云高速开发平台代码生成
查看>>
Linux - 设置SFTP服务用户目录权限
查看>>
Ctrl+Tab
查看>>
JAVA设计模式之【工厂方法模式】
查看>>
[No000034]知乎-长期接收碎片化知识有什么弊端?
查看>>
SVG_资料_坐标转换
查看>>
Windows单机配置Zookeeper环境
查看>>
[CentOS]yum安装postgres和ntfs-3g
查看>>
PHP ob_clean 清空先前输出
查看>>
(翻译)React Container Components
查看>>
Notification的总结
查看>>
pku 2299 Ultra-QuickSort 归并排序求逆序数
查看>>
Python 进度条
查看>>
在chorme中查找多余的css规则
查看>>
搜索引擎选择: Elasticsearch与Solr
查看>>
串口开发
查看>>
RxSwift学习笔记6:Subjects/PublishSubject/BehaviorSubject/ReplaySubject/Variable
查看>>
nohup
查看>>
JAVA设计模式之策略模式 - Strategy
查看>>