jQuery源码学习(二)

回调对象Callbacks

回调对象Callbacks就是用来管理回调函数队列的。

参数说明

它提供几个便捷的处理参数

- once: 确保这个回调列表只执行一次

- memory: 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调

- unique: 确保一次只能添加一个回调(所以在列表中没有重复的回调).

- stopOnFalse: 当一个回调返回false 时中断调用

once和stopOnFalse作用于fire

memory和unique作用于add

once在源码中的实现:

// 以给定的上下文和参数调用所有回调函数
fireWith: function( context, args ) {
//第二次触发的时候fired为true
//stack = !options.once && []
//!fired为false
//如果没有设置once,!options.once && []为true  则再次触发fire( args );
//如果设置了once,!options.once && []为false  则不会再次触发fire( args );
                if ( list && ( !fired || stack ) ) {
                    args = args || [];
                    //args.slice是判断args是不是数组
                    args = [ context, args.slice ? args.slice() : args ];
                    if ( firing ) {//如果正在回调
                        //将参数推入堆栈,等待当前回调结束再调用
                        stack.push( args );
                    } else {//否则直接调用
                        fire( args );
                    }
                }
                return this;
            }

memory在源码中的实现:

// 存在memory就会再次触发fire
else if ( memory ) {
//如果options.memory为true,则将memory做为参数,应用最近增加的回调函数
        firingStart = start;
        fire( memory );
                    }

unique在源码中的实现:

options.unique->如果没有设置unique,不管list中有没有该回调都可以添加

options.unique->如果设置了unique,只有list中没有才可以添加该回调

if ( !options.unique || !self.has( arg ) ) {//确保是否可以重复
        list.push( arg );
    }

stopOnFalse在源码中的实现:

//data[ 0 ]是执行环境
//data[ 1 ]就是fire中的参数
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
    //阻止未来可能由于add所产生的回调
    memory = false;
    break;//由于options.stopOnFalse设置为true,所以当有回调函数返回值为false时退出循环
                }

once_demo:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>once</title>
</head>
<body>
    <script type="text/javascript" src="../jquery-2.1.1.js"></script>
    <script type="text/javascript">
        var cb = $.Callbacks("once");
        function aaa(){
            console.log(1);
        }
        function bbb(){
            console.log(2);
        }
        cb.add(aaa);
        cb.add(bbb);
        cb.fire();//输出1
        cb.fire();//不执行
    </script>
</body>
</html>

memory_demo:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>memory</title>
</head>
<body>
    <script type="text/javascript" src="../jquery-2.1.1.js"></script>
    <script type="text/javascript">
        var cb = $.Callbacks("memory");
        function aaa(){
            console.log(1);
        }
        function bbb(){
            console.log(2);
        }
        cb.add(aaa);
        cb.fire();
        cb.add(bbb);//由于memory有记忆功能,后添加的也能触发
    </script>
</body>
</html>

unique_demo:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Unique</title>
</head>
<body>
    <script type="text/javascript" src="../jquery-2.1.1.js"></script>
    <script type="text/javascript">
        var cb = $.Callbacks("unique");
        function aaa(){
            console.log(1);
        }
        cb.add(aaa);//有效
        cb.add(aaa);//添加不进去
        cb.fire();//输出 1
    </script>
</body>
</html>

源码处理解析:

if ( !options.unique || !self.has( arg ) ) { //确保是否可以重复
    list.push( arg );
}                

如果options.unique为true,并且回调列表中不包含arg,则将arg添加到回调列表中

stopOnFalse_demo:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>stopOnFalse</title>
</head>
<body>
    <script type="text/javascript" src="../jquery-2.1.1.js"></script>
    <script type="text/javascript">
        var cb = $.Callbacks("stopOnFalse");
        function aaa(){
            console.log(1);
            return false;
        }
        function bbb(){
            console.log(2);
        }
        cb.add(aaa);//执行输出1
        cb.add(bbb);//不执行
        cb.fire();
    </script>
</body>
</html>

混合参数_demo:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>混合参数</title>
</head>
<body>
    <script type="text/javascript" src="../jquery-2.1.1.js"></script>
    <script type="text/javascript">
        var cb = $.Callbacks("once memory");
        function aaa(){
            console.log(1);
        }
        function bbb(){
            console.log(2);
        }
        cb.add(aaa);
        cb.fire();//执行输出1 2
        cb.add(bbb);
        cb.fire();//因为once,不执行
    </script>
</body>
</html>

方法介绍

根据jQuery.Callbacks()的API:

  • callbacks.add() 回调列表中添加一个回调或回调的集合。
  • callbacks.disable() 禁用回调列表中的回调
  • callbacks.disabled() 确定回调列表是否已被禁用。
  • callbacks.empty() 从列表中删除所有的回调.
  • callbacks.fire() 用给定的参数调用所有的回调
  • callbacks.fired() 访问给定的上下文和参数列表中的所有回调。
  • callbacks.fireWith() 访问给定的上下文和参数列表中的所有回调。
  • callbacks.has() 确定列表中是否提供一个回调
  • callbacks.lock() 锁定当前状态的回调列表。
  • callbacks.locked() 确定回调列表是否已被锁定。
  • callbacks.remove() 从回调列表中的删除一个回调或回调集合。

延迟对象Deferred

延迟对象是基于回调对象来实现的,用来实现异步的统一管理

when()是用来辅助延迟对象的

大体框架如下:

jQuery.extend({//两个工具方法
            Deferred:function(){},
            when : function(){}
        });

$.Deferred()

有三组对应关系:

  • resolve->done
  • reject->fail
  • notify->progress

前两组对应关系,分别对应相应的状态变化:

- pending->resolved

- pending->rejected

一旦发生两种状态变化中的一种,则状态变为不可变

<body>
    <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
    <script type="text/javascript">
    var cb = $.Callbacks();
    setTimeout(function(){
        alert(111);
        cb.fire();
    },1000);
    //add先把这个函数存到数组里
    //当调用fire的时候再触发
    cb.add(function(){
        alert(222);
    });
    </script>
    <script type="text/javascript">
    //延迟对象-实现对异步的统一管理
    var dfd = $.Deferred();//延迟对象
    setTimeout(function(){
        alert(111);
        dfd.resolve();
    },1000);
    dfd.done(function(){
        alert(222);
    });
    </script>
    <script type="text/javascript">
    setTimeout(function(){
        alert(111);
    },1000);
    //定时器就是异步的
    //延迟对象可以实现从上往下的顺序
    //先弹出1,再弹出2
    alert(222);
    </script>
    <script type="text/javascript">
    var dfd = $.Deferred();
    setTimeout(function(){
        alert(111);
        dfd.reject();
    },1000);
    dfd.fail(function(){
        alert(222);
    });
    </script>
    <script type="text/javascript">
    var dfd = $.Deferred();
    setTimeout(function(){
        alert(111);
        dfd.notify();
    },1000);
    dfd.progress(function(){
        alert(222);
    });
    </script>
</body>
<body>
    <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
    <script type="text/javascript">
    var def = $.Deferred();
    setInterval(function(){
        alert(111);
        def.resolve();
        // def.reject();
        // def.notify();
    },1000);
    def.done(function(){
        alert("成功");//只会弹一次成功
        //因为源码中设置了jQuery.Callbacks("once memory")
    }).fail(function(){
        alert("失败");//只会弹一次失败
        //因为源码中设置了jQuery.Callbacks("once memory")
    }).progress(function(){
        alert("进度中");//每隔一秒都会弹出
    });
    </script>
</body>

deferred和promise的区别

Deferred对象和promise对象都是延迟对象

Deferred

  • resolve
  • reject
  • notify
  • state
  • always
  • then
  • promise
  • pipe
  • done
  • fail
  • progress

promise对象

  • state
  • always
  • then
  • promise
  • pipe
  • done
  • fail
  • progress

通过上面的列举,我们可以看出:deferred比promise多了resolve、reject和notify

<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
function aaa(){
    var dfd = $.Deferred();
    setTimeout(function(){
        dfd.resolve();//不会触发
    },1000);
    return dfd;
}
var newDfd = aaa();
newDfd.done(function(){
    alert("成功");
}).fail(function(){
    alert("失败");//弹出失败
});
newDfd.reject();//会先触发
</script>
</body>

dfd.promise():延迟对象调用promise()方法,返回一个promise对象

promise对象中没有reject()方法

这里为了阻止延迟对象的状态在外部被改变,需要这样来调用dfd.promise()

源码中是这样实现的:

dfd.promise()这里参数为空,所以这里就返回promise对象

promise: function( obj ) {
    return obj != null ? jQuery.extend( obj, promise ) : promise;
                }
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
function aaa(){
    var dfd = $.Deferred();
    setTimeout(function(){
        dfd.resolve();
    },1000);
    return dfd.promise();
}
var newDfd = aaa();
newDfd.done(function(){
    alert("成功");//弹出成功
}).fail(function(){
    alert("失败");
});
newDfd.reject();//会报错
</script>
</body>

always

<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        var dfd = $.Deferred();
        setTimeout(function(){
            //dfd.resolve();
            dfd.reject();
        },1000);
        //不管是resolve还是reject,always总会执行
        dfd.always(function(){
            alert("总会弹出");
        });
    </script>
</body>

源码中是这样写的:

always: function() {
//总会触发是因为既有done又有fail
deferred.done( arguments ).fail( arguments );
            return this;
            //return this表示可以进行链式操作
                }

state

返回状态

resovled或者rejected

<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
    function aaa(){
        var dfd=$.Deferred();
        alert(dfd.state());//pending
        setTimeout(function(){
            dfd.resolve();//resolve()之后,状态变为resolved
            alert(dfd.state());//resolved
        },1000);
        return dfd;
    }
    var newDfd=aaa();
    newDfd.done(function(){
        alert(‘成功‘);
    }).fail(function(){
        alert(‘失败‘);
    });
</script>
</body>

then

<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        var dfd = $.Deferred();
        setTimeout(function(){
            //dfd.resolve();
            dfd.reject();
        },1000);
        //then中对应三种状态
        dfd.then(function(){
            alert("成功");
        },function(){
            alert("失败");
        },function(){
            alert("进行中");
        });
    </script>
</body>

pipe

// then和pipe代码一样,功能不一样
// pipe可以把延迟对象变长
promise.pipe = promise.then;
<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        var dfd = $.Deferred();
        setTimeout(function(){
            //dfd.resolve();
            dfd.resolve(‘hello‘);//可以接收参数传递
        },1000);
        var newDfd = dfd.pipe(function(){
            return arguments[0]+‘你好‘;
        });
        newDfd.done(function(){
            alert(arguments[0]);//hello你好
        });
    </script>
</body>

$.when()

when是用来辅助延迟对象的工具方法

<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        function aaa(){
            var dfd = $.Deferred();
            //dfd.resolve();
            dfd.reject();
            return dfd;
        }
        function bbb(){
            var dfd = $.Deferred();
            dfd.resolve();
            //dfd.reject();
            return dfd;
        }
        /*
        这样写只有aaa()和bbb()同时完成触发才弹出,但是任意一个未完成就可以触发fail()
         */
        //when是针对多个延迟对象的
        $.when(aaa(),bbb()).done(function(){
            alert(‘成功‘);
        }).fail(function(){
            alert(‘失败‘);
        });
    </script>
</body>
<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        function aaa(){
            var dfd = $.Deferred();
            dfd.resolve();
            return dfd;
        }
        function bbb(){
            var dfd = $.Deferred();
            dfd.reject();
            return dfd;  //这里注销后将返回成功,不注销返回失败
        }
        /*
        这样写只有aaa()和bbb()同时完成触发才弹出,但是任意一个未完成就可以触发fail()
         */
        //when是针对多个延迟对象的
        //when中的参数必须是延迟对象才起作用
        //如果不是延迟对象就会自动跳过该参数
        $.when(aaa(),bbb()).done(function(){
            alert(‘成功‘);
        }).fail(function(){
            alert(‘失败‘);
        });
    </script>
</body>
    $.when(aaa(),bbb(),ccc(),ddd()).done(function(){
            alert(‘成功‘);
        });

        aaa()->arguments[0] done()
        bbb()->arguments[1] done()
        ccc()->arguments[2] done()
        ddd()->arguments[3] done()

when是针对多个延迟对象的,比如这里针对四个延迟对象,when的源码中有一个计数器,这里计数器就是4,触发一个延迟对象,计数器减1,当计数器为0时。$.when()会返回一个延迟对象deferred.promise(),然后触发resolve()方法,最后触发done。

时间: 2024-11-06 17:46:27

jQuery源码学习(二)的相关文章

菜鸟的jQuery源码学习笔记(二)

jQuery对象是使用构造函数和原型模式相结合的方式创建的.现在来看看jQuery的原型对象jQuery.prototype: 1 jQuery.fn = jQuery.prototype = { 2 //成员变量和方法 3 } 这里给原型对象起了一个别名叫做jQuery.fn.要注意的是这个jQuery.fn可不是jQuery对象的属性,而是jQuery构造方法本身的属性,它是不会传给它所创建的对象的.如果你在控制台敲$().fn的话输出的结果会是undefined.接下来看看原型对象里面有些

jquery源码学习(二)sizzle部分 【转】

一,sizzle的基本原理 sizzle是jquery选择器引擎模块的名称,早在1.3版本就独立出来,并且被许多其他的js库当做默认的选择器引擎.首先,sizzle最大的特点就是快.那么为什么sizzle当时其他引擎都快了,因为当时其他的引擎都是按照从左到右逐个匹配的方式来进行查找的,而sizzle刚好相反是从右到左找的. 举个简单的例子 “.a .b .c”来说明为什么sizzle比较快.这个例子如果按照从左到右的顺序查找,很明显需要三次遍历过程才能结束,即先在document中查找.a,然后

jquery源码学习

jQuery 源码学习是对js的能力提升很有帮助的一个方法,废话不说,我们来开始学习啦 我们学习的源码是jquery-2.0.3已经不支持IE6,7,8了,因为可以少学很多hack和兼容的方法. jquery-2.0.3的代码结构如下 首先最外层为一个闭包, 代码执行的最后一句为window.$ = window.jquery = jquery 让闭包中的变量暴露倒全局中. 传参传入window是为了便于压缩 传入undefined是为了undifined被修改,他是window的属性,可以被修

jquery源码学习(一)core部分

这一部分是jquery的核心 jquery的构造器 jquery的核心工具函数 构造器 jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQu

jquery源码学习-构造函数(2)

最近几天一直在研究jquery源码,由于水平太低看得昏头转向.本来理解的也不是很深刻,下面就用自己的想法来说下jquery是如何定义构造函数初始化的.如果有什么不对的地方,希望个位高手指出.  一般写构造函数如下 function Aaa(){} Aaa.prototype.init = function(){}; Aaa.prototype.css = function(){}; var a1 = new Aaa(); a1.init(); //初始化 a1.css(); jQuery写法如下

jquery源码学习-初始(1)

最近几天一直在研究jquery源码,由于水平太低看得昏头转向.本来理解的也不是很深刻,下面就用自己的想法来说下jquery是如何定义构造函数初始化的.如果有什么不对的地方,希望个位高手指出. 首先要了解下什么时候自执行匿名函数 ,它的用途就是 相当于创建一个命名空间,只要把自己所有的代码写在这个特殊的函数包装内,外部不能访问,除非你允许. 基本格式 (function(){ //代码 })(); (function(){ var a = 10; function $(){ alert(a); }

jQuery源码学习笔记:总体架构

1.1.自调用匿名函数: (function( window, undefined ) { // jquery code })(window); 这是一个自调用匿名函数,第一个括号内是一个匿名函数,第二个括号立即执行,传参是window. 1.为什么有自调用匿名函数? 通过定义匿名函数,创建了一个"私有"空间,jQuery必须保证创建的变量不能和导入它的程序发生冲突. 2.为什么传入window? 传入window使得window由全局变量变成局部变量,jQuery访问window时,

jQuery源码学习:常用正则表达式

常用的数字正则(严格匹配) 正则 含义 ^[1-9]\d*$ 匹配正整数 ^-[1-9]\d*$ 匹配负整数 ^-?[1-9]\d*$ 匹配整数 /^(([1-9]\d*)|(0+))$/; 匹配非负整数(正整数 + 0) ^-[1-9]\d*|0$ 匹配非正整数(负整数 + 0) /^(([1-9]\d*)|0)\.\d+$/ 匹配正浮点数 ^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 匹配负浮点数 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\

jQuery源码学习笔记:构造jQuery对象

3.1源码结构: (function( window, undefined ) { var jQuery = (function() { // 构建jQuery对象 var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); } // jQuery对象原型 jQuery.fn = jQuery.prototype = { constructor: