自己动手实现jQuery Callbacks完整功能代码详解


Posted in Javascript onNovember 25, 2013

用法和$.Callbacks完全一致 , 但是只是实现了add , remove , fire , empty, has和带参数的构造函数功能,  $.Callbacks 还有disable,disabled, fireWith , fired , lock, locked 方法

 代码如下:

 

 String.prototype.trim = function ()
        {
            return this.replace( /^\s+|\s+$/g, '' );
        };
        // Simulate jQuery.Callbacks object
        function MyCallbacks( options )
        {
            var ops = { once: false, memory: false, unique: false, stopOnFalse: false };
            if ( typeof options === 'string' && options.trim() !== '' )
            {
                var opsArray = options.split( /\s+/ );
                for ( var i = 0; i < options.length; i++ )
                {
                    if ( opsArray[i] === 'once' )
                        ops.once = true;
                    else if ( opsArray[i] === 'memory' )
                        ops.memory = true;
                    else if ( opsArray[i] === 'unique' )
                        ops.unique = true;
                    else if ( opsArray[i] === 'stopOnFalse' )
                        ops.stopOnFalse = true;
                }
            }
            var ar = [];
            var lastArgs = null;
            var firedTimes = 0;
            function hasName( name )
            {
                var h = false;
                if ( typeof name === 'string'
                    && name !== null
                    && name.trim() !== ''
                    && ar.length > 0 )
                {
                    for ( var i = 0; i < ar.length; i++ )
                    {
                        if ( ar[i].name === name )
                        {
                            h = true;
                            break;
                        }
                    }
                }
                return h;
            }
            // add a function
            this.add = function ( fn )
            {
                if ( typeof fn === 'function' )
                {
                    if ( ops.unique )
                    {
                        // check whether it had been added before
                        if ( fn.name !== '' && hasName( fn.name ) )
                        {
                            return this;
                        }
                    }
                    ar.push( fn );
                    if ( ops.memory )
                    {
                        // after added , call it immediately
                        fn.call( this, lastArgs );
                    }
                }
                return this;
            };
            // remove a function
            this.remove = function ( fn )
            {
                if ( typeof ( fn ) === 'function'
                    && fn.name !== ''
                    && ar.length > 0 )
                {
                    for ( var i = 0; i < ar.length; i++ )
                    {
                        if ( ar[i].name === fn.name )
                        {
                            ar.splice( i, 1 );
                        }
                    }
                }
                return this;
            };
            // remove all functions 
            this.empty = function ()
            {
                ar.length = 0;
                return this;
            };
            // check whether it includes a specific function
            this.has = function ( fn )
            {
                var f = false;
                if ( typeof ( fn ) === 'function'
                    && fn.name !== ''
                    && ar.length > 0 )
                {
                    for ( var i = 0; i < ar.length; i++ )
                    {
                        if ( ar[i].name === fn.name )
                        {
                            f = true;
                            break;
                        }
                    }
                }
                return f;
            };
            // invoke funtions it includes one by one 
            this.fire = function ( args )
            {
                if ( ops.once && firedTimes > 0 )
                {
                    return this;
                }
                if ( ar.length > 0 )
                {
                    var r;
                    for ( var i = 0; i < ar.length; i++ )
                    {
                        r = ar[i].call( this, args );
                        if ( ops.stopOnFalse && r === false )
                        {
                            break;
                        }
                    }
                }
                firedTimes++;
                if ( ops.memory )
                {
                    lastArgs = args;
                }
                return this;
            };
        };
 

 测试函数如下:(注意fn1 fn2是匿名函数, fn2返回false , fn3是有“名”函数)

 

 var fn1 = function ( v )
        {
            console.log( 'fn1 ' + ( v || '' ) );
        };
        var fn2 = function ( v )
        {
            console.log( 'fn2 ' + ( v || '' ) );
            return false;
        };
        function fn3( v )
        {
            console.log( 'fn3 ' + ( v || '' ) );
        };
 

 1 . 测试add & fire

var cb=new MyCallbacks();

cb.add(fn1)

cb.add(fn2)

cb.add(fn3)

cb.fire('hello')

输出:

fn1 hello
fn2 hello
fn3 hello

2.测试remove
var cb=new MyCallbacks();

cb.add(fn1)

cb.add(fn2)

cb.add(fn3)

cb.remove(fn1)
cb.fire('hello')
cb.remove(fn3)
cb.fire('hello')
输出:

fn1 hello
fn2 hello
fn3 hello
----------------------------
fn1 hello
fn2 hello

2.测试has
var cb=new MyCallbacks();

cb.add(fn1)

cb.add(fn2)

cb.add(fn3)

cb.has(fn1) 

cb.has(fn3) 

输出:

false

---------------

true

3.测试带参数的构造函数 : once

var cb=new MyCallbacks('once')

cb.add(fn1)

cb.fire('hello')

cb.fire('hello')

cb.add(fn2)

cb.fire('hello')

输出:

hello

-------------------

------------------

------------------------------

4.测试带参数的构造函数 : memory

 var cb=new MyCallbacks('memory')

cb.add(fn1)

cb.fire('hello') // 输出 : fn1 hello

cb.add(fn2) // 输出 : fn2 hello

cb.fire('hello')

 输出 :

 fn1 hello

 fn2 hello

5.测试带参数的构造函数 : stopOnFalse

var cb=new MyCallbacks('stopOnFalse')

cb.add(fn1)

cb.add(fn2)

cb.add(fn3)

cb.fire('hello')

输出:

fn1 hello
fn2 hello
6.测试带参数的构造函数 :unique

var cb=new MyCallbacks('unique')

 

b.add(fn3)

b.add(fn3)

cb.fire('hello')

输出:

fn3 hello

 

7. 测试带组合参数的构造函数:四个设置参数可以随意组合,一下只测试全部组合的情况, 不然要写16个测试用例 T_T

var cb=new MyCallbacks('once memory unique stopOnFalse')

cb.add(fn1) // 输出: fn1

cb.add(fn2) // 输出: fn2

cb.add(fn3) //  输出: fn3

cb.fire('hello')

输出:

fn1 hello
fn2 hello
cb.fire('hello') // 输出:没有输出

 

以下是官方API 文档:

Description: A multi-purpose callbacks list object that provides a powerful way to manage callback lists.The $.Callbacks() function is internally used to provide the base functionality behind the jQuery $.ajax() and$.Deferred() components. It can be used as a similar base to define functionality for new components.

构造函数 : jQuery.Callbacks( flags )

flags
Type: String
An optional list of space-separated flags that change how the callback list behaves.
Possible flags:
once: Ensures the callback list can only be fired once (like a Deferred).
memory: Keeps track of previous values and will call any callback added after the list has been fired right away with the latest "memorized" values (like a Deferred).
unique: Ensures a callback can only be added once (so there are no duplicates in the list).
stopOnFalse: Interrupts callings when a callback returns false.
By default a callback list will act like an event callback list and can be "fired" multiple times.

Two specific methods were being used above: .add() and .fire(). The .add() method supports adding new callbacks to the callback list, while the .fire() method executes the added functions and provides a way to pass arguments to be processed by the callbacks in the same list.

利用Callbacks 实现发布订阅模式 pub/sub: (官方文档)

var topics = {};
        jQuery.Topic = function ( id )
        {
            var callbacks,
                method,
                topic = id && topics[id];
            if ( !topic )
            {
                callbacks = jQuery.Callbacks();
                topic = {
                    publish: callbacks.fire,
                    subscribe: callbacks.add,
                    unsubscribe: callbacks.remove
                };
                if ( id )
                {
                    topics[id] = topic;
                }
            }
            return topic;
        };

使用

$.Topic( 'mailArrived' ).subscribe( function ( e )
        {
            console.log( 'Your have new email! ' );
            console.log( "mail title : " + e.title );
            console.log( "mail content : " + e.content );
        }
        );
        $.Topic( 'mailArrived' ).publish( { title: 'mail title', content: 'mail content' } );

实现了其余的全部功能 :callbacks.disable , callbacks.disabled,   callbacks.fired,callbacks.fireWith, callbacks.lock, callbacks.locked ,然后重构了下代码结构, 将实现放入了匿名函数内, 然后通过工厂方法 window.callbacks 返回实例,以免每次使用必须 new .

具体代码如下, 有兴趣和时间的可以对照jQuery版本的Callbacks对比下 :

( function ( window, undefined )
        {
            // Simulate jQuery.Callbacks object
            function Callbacks( options )
            {
                var ops = { once: false, memory: false, unique: false, stopOnFalse: false },
                    ar = [],
                    lastArgs = null,
                    firedTimes = 0,
                    _disabled = false,
                    _locked = false;
                if ( typeof options === 'string' && options.trim() !== '' )
                {
                    var opsArray = options.split( /\s+/ );
                    for ( var i = 0; i < options.length; i++ )
                    {
                        if ( opsArray[i] === 'once' )
                            ops.once = true;
                        else if ( opsArray[i] === 'memory' )
                            ops.memory = true;
                        else if ( opsArray[i] === 'unique' )
                            ops.unique = true;
                        else if ( opsArray[i] === 'stopOnFalse' )
                            ops.stopOnFalse = true;
                    }
                }
                function hasName( name )
                {
                    var h = false;
                    if ( typeof name === 'string'
                        && name !== null
                        && name.trim() !== ''
                        && ar.length > 0 )
                    {
                        for ( var i = 0; i < ar.length; i++ )
                        {
                            if ( ar[i].name === name )
                            {
                                h = true;
                                break;
                            }
                        }
                    }
                    return h;
                }
                // add a function
                this.add = function ( fn )
                {
                    if ( typeof fn === 'function' )
                    {
                        if ( ops.unique )
                        {
                            // check whether it had been added before
                            if ( fn.name !== '' && hasName( fn.name ) )
                            {
                                return this;
                            }
                        }
                        ar.push( fn );
                        if ( ops.memory )
                        {
                            // after added , call it immediately
                            fn.call( this, lastArgs );
                        }
                    }
                    return this;
                };
                // remove a function
                this.remove = function ( fn )
                {
                    if ( typeof ( fn ) === 'function'
                        && fn.name !== ''
                        && ar.length > 0 )
                    {
                        for ( var i = 0; i < ar.length; i++ )
                        {
                            if ( ar[i].name === fn.name )
                            {
                                ar.splice( i, 1 );
                            }
                        }
                    }
                    return this;
                };
                // remove all functions 
                this.empty = function ()
                {
                    ar.length = 0;
                    return this;
                };
                // check whether it includes a specific function
                this.has = function ( fn )
                {
                    var f = false;
                    if ( typeof ( fn ) === 'function'
                        && fn.name !== ''
                        && ar.length > 0 )
                    {
                        for ( var i = 0; i < ar.length; i++ )
                        {
                            if ( ar[i].name === fn.name )
                            {
                                f = true;
                                break;
                            }
                        }
                    }
                    return f;
                };
                this.disable = function ()
                {
                    _disabled = true;
                    return this;
                };
                this.disabled = function ()
                {
                    return _disabled;
                };
                this.fired = function ()
                {
                    return firedTimes > 0;
                };
                function _fire( context, args )
                {
                    if ( _disabled || ops.once && firedTimes > 0 || _locked )
                    {
                        return;
                    }
                    if ( ar.length > 0 )
                    {
                        var r;
                        for ( var i = 0; i < ar.length; i++ )
                        {
                            r = ar[i].call( context, args );
                            if ( ops.stopOnFalse && r === false )
                            {
                                break;
                            }
                        }
                    }
                    firedTimes++;
                    if ( ops.memory )
                    {
                        lastArgs = args;
                    }
                };
                this.fireWith = function ( context, args )
                {
                    context = context || this;
                    _fire( context, args );
                    return this;
                };
                this.fire = function ( args )
                {
                    _fire( this, args );
                    return this;
                };
                this.lock = function ()
                {
                    _locked = true;
                    return this;
                };
                this.locked = function ()
                {
                    return _locked;
                };
            };
            // exposed to global as a factory method
            window.callbacks = function ( options )
            {
                return new Callbacks( options );
            };
        } )( window );
Javascript 相关文章推荐
jQuery的Ajax的自动完成功能控件简要说明
Feb 22 Javascript
jQuery选择器中含有空格的使用示例及注意事项
Aug 25 Javascript
js的回调函数详解
Jan 05 Javascript
详解JavaScript数组和字符串中去除重复值的方法
Mar 07 Javascript
jquery通过name属性取值的简单实现方法
Jun 20 Javascript
javaScript如何跳出多重循环break、continue
Sep 01 Javascript
javascript少儿编程关于返回值的函数内容
May 27 Javascript
详解vue-router 命名路由和命名视图
Jun 01 Javascript
vue路由切换之淡入淡出的简单实现
Oct 31 Javascript
ES6 Object.assign()的用法及其使用
Jan 18 Javascript
使用 Opentype.js 生成字体子集的实例代码详解
May 25 Javascript
angular共享依赖的解决方案分享
Oct 15 Javascript
写JQuery插件的基本知识
Nov 25 #Javascript
JavaScript动态操作表格实例(添加,删除行,列及单元格)
Nov 25 #Javascript
用javascript删除当前行,添加行(示例代码)
Nov 25 #Javascript
如何通过javascript操作web控件的自定义属性
Nov 25 #Javascript
利用js实现前台动态添加文本框,后台获取文本框内容(示例代码)
Nov 25 #Javascript
js导入导出excel(实例代码)
Nov 25 #Javascript
用javascript添加控件自定义属性解析
Nov 25 #Javascript
You might like
php ctype函数中文翻译和示例
2014/03/21 PHP
WordPress中获取页面链接和标题的相关PHP函数用法解析
2015/12/17 PHP
PHP实现模拟http请求的方法分析
2017/12/20 PHP
Ctrl+Enter提交内容信息
2006/06/26 Javascript
打开新窗口关闭当前页面不弹出关闭提示js代码
2013/03/18 Javascript
浅析LigerUi开发中谨慎载入common.css文件
2013/07/09 Javascript
js判断文本框剩余可输入字数的方法
2015/02/04 Javascript
JavaScript中的函数声明和函数表达式区别浅析
2015/03/27 Javascript
[原创]Bootstrap 中下拉菜单修改成鼠标悬停直接显示
2016/04/14 Javascript
javascript日期比较方法实例分析
2016/06/17 Javascript
javaScript 事件绑定、事件冒泡、事件捕获和事件执行顺序整理总结
2016/10/10 Javascript
关于微信jssdk实现多图片上传的一点心得分享
2016/12/13 Javascript
JS实现超简单的汉字转拼音功能示例
2016/12/22 Javascript
php输出全部gb2312编码内的汉字方法
2017/03/04 Javascript
关于Vue实现组件信息的缓存问题
2017/08/23 Javascript
谈谈为什么你的 JavaScript 代码如此冗长
2019/01/30 Javascript
javascript异常处理实现原理详解
2020/02/17 Javascript
python元组操作实例解析
2014/09/23 Python
详解Django中的权限和组以及消息
2015/07/23 Python
python中私有函数调用方法解密
2016/04/29 Python
python之matplotlib学习绘制动态更新图实例代码
2018/01/23 Python
基于Python执行dos命令并获取输出的结果
2019/12/30 Python
Python内置数据类型list各方法的性能测试过程解析
2020/01/07 Python
Python3连接Mysql8.0遇到的问题及处理步骤
2020/02/17 Python
Python3自定义http/https请求拦截mitmproxy脚本实例
2020/05/11 Python
详解python对象之间的交互
2020/09/29 Python
Python自动化办公Excel模块openpyxl原理及用法解析
2020/11/05 Python
利用HTML5实现使用按钮控制背景音乐开关
2015/09/21 HTML / CSS
澳大利亚首个在线预订旅游网站:Wotif
2017/07/19 全球购物
美国存储和组织商店:The Container Store
2017/08/16 全球购物
企业管理专业个人求职信范文
2013/09/24 职场文书
先进典型事迹材料
2014/12/29 职场文书
万能检讨书
2015/01/27 职场文书
社区六一儿童节活动总结
2015/02/11 职场文书
微信小程序结合ThinkPHP5授权登陆后获取手机号
2021/11/23 PHP
MySQL索引 高效获取数据的数据结构
2022/05/02 MySQL