自己动手实现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 相关文章推荐
javascript创建数组之联合数组的使用方法示例
Dec 26 Javascript
js的延迟执行问题分析
Jun 23 Javascript
jQuery+ajax实现鼠标单击修改内容的思路
Jun 29 Javascript
vue jsx 使用指南及vue.js 使用jsx语法的方法
Nov 11 Javascript
浅谈node模块与npm包管理工具
Jan 03 Javascript
解决iView中时间控件选择的时间总是少一天的问题
Mar 15 Javascript
微信小程序实现省市区三级地址选择
Jun 21 Javascript
layui table动态表头 改变表格头部 重新加载表格的方法
Sep 21 Javascript
解决vue-cli 打包后自定义动画未执行的问题
Nov 12 Javascript
Quasar Input:type=&quot;number&quot; 去掉上下小箭头 实现加减按钮样式功能
Apr 09 Javascript
详解JavaScript 作用域
Jul 14 Javascript
JS数组方法some、every和find的使用详情
Oct 05 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开发中的安全防范知识详解
2013/06/06 PHP
PHP封装的字符串加密解密函数
2015/12/18 PHP
php简单实现多语言切换的方法
2016/05/09 PHP
php mysql_list_dbs()函数用法示例
2017/03/29 PHP
PHP实现的文件上传类与用法详解
2017/07/05 PHP
tp5.1 框架查询表达式用法详解
2020/05/25 PHP
ThinkPHP5.1验证码功能实现的示例代码
2020/06/08 PHP
JS对img进行操作(换图片/切图/轮换/停止)
2013/04/17 Javascript
JS+CSS实现分类动态选择及移动功能效果代码
2015/10/19 Javascript
Vue 过渡实现轮播图效果
2017/03/27 Javascript
vue select组件的使用与禁用实现代码
2018/04/10 Javascript
angular将html代码输出为内容的实例
2018/09/30 Javascript
Vue之beforeEach非登录不能访问的实现(代码亲测)
2019/07/18 Javascript
koa2+vue实现登陆及登录状态判断
2019/08/15 Javascript
layer实现弹出层自动调节位置
2019/09/05 Javascript
微信小程序停止其他视频播放当前视频的实例代码
2019/12/25 Javascript
[02:50]【扭转乾坤,只此一招】DOTA2永雾林渊版本开启新篇章
2020/12/22 DOTA
使用Python脚本将绝对url替换为相对url的教程
2015/04/24 Python
举例讲解Python设计模式编程的代理模式与抽象工厂模式
2016/01/16 Python
详解python开发环境搭建
2016/12/16 Python
Python 专题三 字符串的基础知识
2017/03/19 Python
详解Python字符串切片
2019/05/20 Python
python 使用matplotlib 实现从文件中读取x,y坐标的可视化方法
2019/07/04 Python
python使用Matplotlib改变坐标轴的默认位置
2019/10/18 Python
python爬虫实例之获取动漫截图
2020/05/31 Python
python中entry用法讲解
2020/12/04 Python
selenium框架中driver.close()和driver.quit()关闭浏览器
2020/12/08 Python
纬创Java面试题笔试题
2014/10/02 面试题
关键字throw与throws的用法差异
2016/11/22 面试题
小学生学习雷锋倡议书
2014/05/15 职场文书
防灾减灾宣传标语
2014/10/07 职场文书
教师业务学习材料
2014/12/16 职场文书
烟台的海导游词
2015/02/02 职场文书
大学生简历自我评价2015
2015/03/03 职场文书
文化大革命观后感
2015/06/17 职场文书
缓存替换策略及应用(以Redis、InnoDB为例)
2021/07/25 Redis