自己动手实现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 10 Javascript
js实现图片拖动改变顺序附图
May 13 Javascript
Node.js的包详细介绍
Jan 14 Javascript
Javascript中的apply()方法浅析
Mar 15 Javascript
js中函数声明与函数表达式
Jun 03 Javascript
Javascript模仿淘宝信用评价实例(附源码)
Nov 26 Javascript
JavaScript学习小结(7)之JS RegExp
Nov 29 Javascript
浅谈jquery中next与siblings的区别
Oct 27 Javascript
轻松实现jQuery添加删除按钮Click事件
Mar 13 Javascript
echarts饼图扇区添加点击事件的实例
Oct 16 Javascript
解决vue 格式化银行卡(信用卡)每4位一个符号隔断的问题
Sep 14 Javascript
js核心基础之闭包的应用实例分析
May 11 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获得文件扩展名三法
2006/11/25 PHP
PHP __autoload函数(自动载入类文件)的使用方法
2012/02/04 PHP
Session的工作机制详解和安全性问题(PHP实例讲解)
2014/04/10 PHP
destoon切换城市后实现logo旁边显示地区名称的方法
2014/08/21 PHP
PHP中使用数组指针函数操作数组示例
2014/11/19 PHP
Laravel 实现密码重置功能
2018/02/23 PHP
简约JS日历控件 实例代码
2013/07/12 Javascript
两种不同的方法实现js对checkbox进行全选和反选
2014/05/13 Javascript
详解AngularJS的通信机制
2015/06/18 Javascript
jQuery实现下拉框左右移动(全部移动,已选移动)
2016/04/15 Javascript
用vue封装插件并发布到npm的方法步骤
2017/10/18 Javascript
vue.js select下拉框绑定和取值方法
2018/03/03 Javascript
jQuery实现的点击按钮改变样式功能示例
2018/07/21 jQuery
vue 中使用 watch 出现了如下的报错的原因分析
2019/05/21 Javascript
一行JavaScript代码如何实现瀑布流布局
2020/12/11 Javascript
[09:37]DOTA2卡尔工作室 英雄介绍圣堂刺客篇
2013/06/13 DOTA
详解常用查找数据结构及算法(Python实现)
2016/12/09 Python
利用aardio给python编写图形界面
2017/08/21 Python
python实现搜索文本文件内容脚本
2018/06/22 Python
python GUI库图形界面开发之PyQt5动态(可拖动控件大小)布局控件QSplitter详细使用方法与实例
2020/03/06 Python
python接入支付宝的实例操作
2020/07/20 Python
.NET常见笔试题集
2012/12/01 面试题
客服工作职责
2013/12/11 职场文书
《歌唱二小放牛郎》教学反思
2014/04/19 职场文书
诉前财产保全担保书
2014/05/20 职场文书
房产公证委托书范本
2014/09/20 职场文书
2014新生大学四年计划书
2014/09/21 职场文书
四风自我剖析材料
2014/09/30 职场文书
防灾减灾宣传标语
2014/10/07 职场文书
2014年学校法制宣传日活动总结
2014/11/01 职场文书
谢师宴答谢词
2015/01/05 职场文书
党员评议自我评价
2015/03/03 职场文书
使用Pytorch实现two-head(多输出)模型的操作
2021/05/28 Python
Java 中的 Unsafe 魔法类的作用大全
2021/06/26 Java/Android
Golang 切片(Slice)实现增删改查
2022/04/22 Golang
mysql sql常用语句大全
2022/06/21 MySQL