自己动手实现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 1.3 和 Validation 验证插件1.5.1
Jul 09 Javascript
JavaScript继承方式实例
Oct 29 Javascript
javascript变量作用域使用中常见错误总结
Mar 26 Javascript
$(&quot;&quot;).click与onclick的区别示例介绍
Sep 25 Javascript
jQuery模拟原生态App上拉刷新下拉加载更多页面及原理
Aug 10 Javascript
Bootstrap 最常用的JS插件系列总结(图片轮播、标签切换等)
Jul 14 Javascript
Vuejs第七篇之Vuejs过渡动画案例全面解析
Sep 05 Javascript
JavaScript取得gridview中获取checkbox选中的值
Jul 24 Javascript
微信小程序 五星评分的实现实例
Aug 04 Javascript
ionic选择多张图片上传的示例代码
Oct 10 Javascript
Bootstrap 树控件使用经验分享(图文解说)
Nov 06 Javascript
从Node.js事件触发器到Vue自定义事件的深入讲解
Jun 26 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版(2)
2006/10/09 PHP
smarty获得当前url的方法分享
2014/02/14 PHP
php使用pack处理二进制文件的方法
2014/07/03 PHP
PHP利用超级全局变量$_GET来接收表单数据的实例
2016/11/05 PHP
JS 删除字符串最后一个字符的实现代码
2014/02/20 Javascript
两种不同的方法实现js对checkbox进行全选和反选
2014/05/13 Javascript
javaScript使用EL表达式的几种方式
2014/05/27 Javascript
jQuery实现的Div窗口震动特效
2014/06/09 Javascript
js实现按钮加背景图片常用方法
2014/11/01 Javascript
javascript结合Flexbox简单实现滑动拼图游戏
2016/02/18 Javascript
vue-cli脚手架搭建的项目去除eslint验证的方法
2018/09/29 Javascript
vue项目搭建以及全家桶的使用详细教程(小结)
2018/12/19 Javascript
基于mpvue搭建微信小程序项目框架的教程详解
2019/04/10 Javascript
jQuery属性选择器用法实例分析
2019/06/28 jQuery
JavaScript实现图片上传并预览并提交ajax
2019/09/30 Javascript
python中合并两个文本文件并按照姓名首字母排序的例子
2014/04/25 Python
Python使用MD5加密字符串示例
2014/08/22 Python
简单使用Python自动生成文章
2014/12/25 Python
python使用pycharm环境调用opencv库
2018/02/11 Python
python使用tcp实现局域网内文件传输
2020/03/20 Python
Python Django 添加首页尾页上一页下一页代码实例
2019/08/21 Python
Python高级编程之消息队列(Queue)与进程池(Pool)实例详解
2019/11/01 Python
基于Numba提高python运行效率过程解析
2020/03/02 Python
python3中sorted函数里cmp参数改变详解
2020/03/12 Python
python实现扑克牌交互式界面发牌程序
2020/04/22 Python
python如何进行矩阵运算
2020/06/05 Python
Bluebella美国官网:英国性感内衣品牌
2018/10/04 全球购物
写一个方法1000的阶乘
2012/11/21 面试题
社区学习十八大感想
2014/01/22 职场文书
村居抓节水倡议书
2014/05/19 职场文书
优秀班主任经验交流材料
2014/06/02 职场文书
体育课外活动总结
2014/07/08 职场文书
清明节扫墓活动总结
2015/02/09 职场文书
人事任命书范本
2015/09/21 职场文书
分析Python感知线程状态的解决方案之Event与信号量
2021/06/16 Python
Python  lambda匿名函数和三元运算符
2022/04/19 Python