自己动手实现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 相关文章推荐
js控制div及网页相关属性的代码
Dec 19 Javascript
JavaScript实现网页图片等比例缩放实现代码及调用方式
Feb 25 Javascript
自己写了一个展开和收起的多更能型的js效果
Mar 05 Javascript
多种方式实现JS调用后台方法进行数据交互
Aug 20 Javascript
浅析jQuery中调用ajax方法时在不同浏览器中遇到的问题
Jun 11 Javascript
轻松学习jQuery插件EasyUI EasyUI创建树形网络(1)
Nov 30 Javascript
jQuery的deferred对象使用详解
Sep 25 Javascript
解析AngularJS中get请求URL出现的跨域问题
Dec 01 Javascript
vue2.0学习之axios的封装与vuex介绍
May 28 Javascript
javascript异步编程的六种方式总结
May 17 Javascript
layui的layedit富文本赋值方法
Sep 18 Javascript
vue项目使用$router.go(-1)返回时刷新原来的界面操作
Jul 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动态生成虚拟现实VRML网页
2006/10/09 PHP
CodeIgniter框架中_remap()使用方法2例
2014/03/10 PHP
PHP基于php_imagick_st-Q8.dll实现JPG合成GIF图片的方法
2014/07/11 PHP
PHP获取一年中每个星期的开始和结束日期的方法
2015/02/12 PHP
php生成zip文件类实例
2015/04/07 PHP
php按单词截取字符串的方法
2015/04/07 PHP
Yii隐藏URL中index.php的方法
2016/07/12 PHP
php 中的closure用法详解
2017/06/12 PHP
PHP实现redis限制单ip、单用户的访问次数功能示例
2018/06/16 PHP
对YUI扩展的Gird组件 Part-1
2007/03/10 Javascript
jquery 表单取值常用代码
2009/12/22 Javascript
匹配任意字符的正则表达式写法
2010/04/29 Javascript
javascript 三种方法实现获得和设置以及移除元素属性
2013/03/20 Javascript
jquery京东商城双11焦点图多图广告特效代码分享
2015/09/06 Javascript
基于jquery实现全屏滚动效果
2015/11/26 Javascript
vue.js入门教程之计算属性
2016/09/01 Javascript
jQuery内容筛选选择器实例代码
2017/02/06 Javascript
Nodejs高扩展性的模板引擎 functmpl简介
2017/02/13 NodeJs
Vue自定义事件(详解)
2017/08/19 Javascript
详解使用nvm管理多版本node的方法
2017/08/30 Javascript
详解Immutable及 React 中实践
2018/03/01 Javascript
微信小程序使用template标签实现五星评分功能
2018/11/03 Javascript
Vue实现数据表格合并列rowspan效果
2020/11/30 Javascript
vue eslint简要配置教程详解
2019/07/26 Javascript
JS实现简易计算器
2020/02/14 Javascript
JS算法教程之字符串去重与字符串反转
2020/12/15 Javascript
python函数的5种参数详解
2017/02/24 Python
Python对列表的操作知识点详解
2019/08/20 Python
python图片验证码识别最新模块muggle_ocr的示例代码
2020/07/03 Python
HTML 5 标签、属性、事件及浏览器兼容性速查表 附打包下载
2012/10/20 HTML / CSS
Unix控制后台进程都有哪些进程
2016/09/22 面试题
财务总监管理职责范文
2014/03/09 职场文书
建房协议书
2014/04/11 职场文书
文明寄语大全
2014/04/11 职场文书
单身证明范本
2015/06/15 职场文书
百年校庆宣传标语口号
2015/12/26 职场文书