zoukankan      html  css  js  c++  java
  • jQuery.API源码深入剖析以及应用实现(2) - jQuery对象访问和数据缓存

    前言

    上篇主要介绍JQuery的核心函数的原理机制,这篇将开始介绍jQuery对象访问和数据缓存原理,主要内容包括:

      

     

    分析

    一、jQuery对象访问

    1. 【each(callback)

    例子:

    HTML代码 jQuery代码 运行结果
    <img/><img/>

    $("img").each(function(i){
       this.src = "test" + i + ".jpg";
    });

    [ <img src="test0.jpg" />, <img src="test1.jpg" /> ]
    <img/><img/>

    $("img").each(function(){
      $(this).toggleClass("example");
    });

    切换样式example

    现在来看each方法的具体实现如下:

    jQuery.fn = jQuery.prototype = {
        each: 
    function( callback, args ) {
            
    return jQuery.each( this, callback, args );
        }
    }

    可以看到它返回的是全局的each方法,并且将自身jQuery对象做为参数给它,全局的each方法的具体实现如下:

    // args 作为内部成员的调用来使用
    each: function( object, callback, args ) {
        
    var name, i = 0, length = object.length;  // 当object为jQuery对象时,length非空

        
    if ( args ) {
            
    if ( length === undefined ) {
                
    for ( name in object )
                    
    if ( callback.apply( object[ name ], args ) === false )
                        
    break;
            } 
    else
                
    for ( ; i < length; )
                    
    if ( callback.apply( object[ i++ ], args ) === false )
                        
    break
        
    // 以下是客户端程序进行调用
        } else {
            
    if ( length === undefined ) {
                
    for ( name in object )
                    
    if ( callback.call( object[ name ], name, object[ name ] ) === false )
                        
    break;
            } 
    else
                
    for ( var value = object[0];
                    i 
    < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
        } 

        
    return object;
    }

    现在我们关注下 for ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} 这句代码;

    其中object[0]取得jQuery对象中的第一个DOM元素,通过for循环,得到遍历整个jQuery对象中对应的每个DOM元素,通过callback.call( value,i,value); 将callback的this对象指向value对象,并且传递两个参数,i表示索引值,value表示DOM元素;其中callback是类似于 function(index, elem) { ... } 的方法。所以就得到 $("...").each(function(index, elem){ ... });

     

    2. 【size()】和【length

    这两个都是得到jQuery 对象中元素的个数,具体实现如下:

    size: function() {
        
    return this.length;
    }

     

    3. 【selector】和【context

    selector返回传给jQuery()的原始选择器。

    context返回传给jQuery()的原始的DOM节点内容,即jQuery()的第二个参数。如果没有指定,那么context指向当前的文档(document)。

    这两个属性在上一篇文章也有提及,请参考jQuery.API源码深入剖析以及应用实现(2) - 对象访问和数据缓存 ,这里不再敖述。


    4. 【get()】和【get(index)

    get()取得所有匹配的 DOM 元素集合。

    get(index)取得其中一个匹配的DOM元素,index表示取得第几个匹配的元素。

    $(this).get(0)与$(this)[0]等价。

    现在看下get方法的具体实现如下:

    get: function( num ) {
        
    return num === undefined ? 

            
    // 当num为undefined时,返回所有匹配的DOM元素集合
            jQuery.makeArray( this ) : 

            
    // 当num不为undefined时,返回第num+1个匹配的DOM元素
            this[ num ];
    }


    当不包含num时,调用jQuery.makeArray方法,具体实现如下:

    makeArray: function( array ) {
        
    var ret = []; 

        
    if( array != null ){
            
    var i = array.length;
            
    // The window, strings (and functions) also have 'length'
            if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
                ret[
    0= array;
            
    else
                
    while( i )
                    ret[
    --i] = array[i];
        } 

        
    return ret;
    }

    可以看出这里的array为jQuery对象,因此执行 while( i ) ret[--i] = array[i]; , 返回的是以array所有匹配的DOM元素所组成的数组。这和前面的定义相一致。

    当包含num时,直接返回 this[ num ],所以这样验证了 $(this).get(0)与$(this)[0]等价 的说明。


    5. 【index(subject)】 

    搜索与参数表示的对象匹配的元素,并返回相应元素的索引值。

    例子

    HTML代码 jQuery代码
    <div id="foobar"><div></div><div id="foo"></div></div>

    $("div").index($('#foobar')[0]) // 0
    $("div").index($('#foo')[0]) // 2
    $("div").index($('#foo')) // -1

    现在看下index方法的具体实现如下:

    index: function( elem ) { 

        
    return jQuery.inArray(
            
    // 如果elem为一个jQuery对象,那么得到的是第一个匹配的DOM元素
            elem && elem.jquery ? elem[0] : elem
        , 
    this );
    }

    继续查看jQuery.inArray方法,具体实现如下:

    inArray: function( elem, array ) {
        
    for ( var i = 0, length = array.length; i < length; i++ )
        
    // Use === because on IE, window == document
            if ( array[ i ] === elem )
                
    return i; 

        
    return -1;
    }

    一目了然,返回elem的索引值。


    二、数据缓存

    1. 【data(name)】和【data(name,value)

    data(name)返回元素上储存的相应名字的数据,可以用data(name, value)来设定。

    data(name,value)在元素上存放数据,同时也返回value。

    例子

    HTML代码 jQuery代码
    <div></div>

    $("div").data("blah"); // undefined
    $("div").data("blah", "hello"); // blah设置为hello
    $("div").data("blah"); // hello
    $("div").data("blah", 86); // 设置为86
    $("div").data("blah"); // 86
    $("div").removeData("blah"); //移除blah
    $("div").data("blah"); // undefined

    <div></div>

    $("div").data("test", { first: 16, last: "pizza!" });
    $("div").data("test").first //16;
    $("div").data("test").last //pizza!;

    现在来看看data方法的具体实现:

    jQuery.fn.extend({
        data: 
    function( key, value ){
            
    var parts = key.split(".");
            parts[
    1= parts[1? "." + parts[1] : ""

            
    if ( value === undefined ) {
                
    var data = this.triggerHandler("getData" + parts[1+ "!", [parts[0]]); 

                
    if ( data === undefined && this.length )
                    data 
    = jQuery.data( this[0], key ); 

                
    return data === undefined && parts[1?
                    
    this.data( parts[0] ) :
                    data;
            } 
    else
                
    return this.trigger("setData" + parts[1+ "!", [parts[0], value]).each(function(){
                    jQuery.data( 
    this, key, value );
                });
        }, 

        removeData: 
    function( key ){
            
    return this.each(function(){
                jQuery.removeData( 
    this, key );
            });
        }
    });


    当我们要在元素上存放数据的时候,比如 $("div").data("blah","hello"); 将执行这句代码:

    return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){jQuery.data( this, key, value );});

    我们看下jQuery.data(this,key,value);这句代码,继续展开jQuery.data方法的具体实现以及相关其他代码:

    function now(){
        
    return +new Date;
    }
    var expando = "jQuery" + now(), uuid = 0, windowData = {}; 

    jQuery.extend({
        cache: {},
        data: 
    function( elem, name, data ) {
            elem 
    = elem == window ?
                windowData :
                elem; 

            
    var id = elem[ expando ]; 

            
    // 在元素上产生唯一的ID
            if ( !id )
                id 
    = elem[ expando ] = ++uuid; 

            
    // 当我们试着访问一个键是否含有值的时候,如果不存在jQuery.cache[id]值, 初始化jQuery.cache[id]值
            if ( name && !jQuery.cache[ id ] )
                jQuery.cache[ id ] 
    = {}; 

            
    // 防止一个undefined的值覆盖jQuery.cache对象
            if ( data !== undefined )
                jQuery.cache[ id ][ name ] 
    =
     data; 

            
    return name ?
                jQuery.cache[ id ][ name ] :
                id;
        }
    });

    其中这句代码

    if ( data !== undefined )
                jQuery.cache[ id ][ name ] = data;

    将data存储在cache对象中。

    当我们需要返回元素上储存的相应名字的数据的时候,比如 $("div").data("blah"); 主要将执行这句代码:

    data = jQuery.data( this[0], key );

    最后将返回一个保存在 jQuery.cache[ id ][ name ] 中的数据。


    2. 【removeData(name)

    在元素上移除存放的数据。具体实现如下:

    removeData: function( key ){
        
    return this.each(function(){
            jQuery.removeData( 
    this, key );
        });
    }

    继续展开jQuery.removeData方法的具体实现:

    removeData: function( elem, name ) {
        elem 
    = elem == window ?
            windowData :
            elem; 

        
    var id = elem[ expando ]; 

        
    if ( name ) {
            
    // elem是否存在元素cache
            if ( jQuery.cache[ id ] ) {
                
    // 移除一个具有特定键的元素数据
                delete jQuery.cache[ id ][ name ]; 

                
    // 将键值置空,准备移除元素cache
                name = ""

                
    for ( name in jQuery.cache[ id ] )
                    
    break

                
    if ( !name )
                    jQuery.removeData( elem );
            } 

        } 
    else {
            
    // Clean up the element expando
            try {
                
    delete elem[ expando ];
            } 
    catch(e){
                
    // IE has trouble directly removing the expando
                // but it's ok with using removeAttribute
                if ( elem.removeAttribute )
                    elem.removeAttribute( expando );
            } 

            
    // 完全移除元素cache
            delete jQuery.cache[ id ];
        }
    }

    通过调用 delete jQuery.cache[ id ][ name ];  和 delete jQuery.cache[ id ];,移除所有该元素上的cache数据;


    3. 【queue([name])】,【queue([name],callback)】和【queue([name],queue

    例子

    HTML 代码 jQuery 代码
      <style>
            div { margin:3px; 40px; height:40px;
            position:absolute; left:0px; top:30px;
            background:green; display:none; }
            div.newcolor { background:blue; }
            span { color:red; }
            </style>
            <button id="show">Show Length of Queue</button>
            <span></span>
            <div></div>
    $("#show").click(function () {
            var n = $("div").queue("fx");
            $("span").text("Queue length is: " + n.length);
            });
            function runIt() {
            $("div").show("slow");
            $("div").animate({left:'+=200'},2000);
            $("div").slideToggle(1000);
            $("div").slideToggle("fast");
            $("div").animate({left:'-=200'},1500);
            $("div").hide("slow");
            $("div").show(1200);
            }
            runIt();
      <style>
            div { margin:3px; 40px; height:40px;
            position:absolute; left:0px; top:30px;
            background:green; display:none; }
            div.newcolor { background:blue; }
            </style>
            Click here...
            <div></div>
    $(document.body).click(function () {
            $("div").show("slow");
            $("div").animate({left:'+=200'},2000);
            $("div").queue(function () {
            $(this).addClass("newcolor");
            $(this).dequeue();
            });
            $("div").animate({left:'-=200'},500);
            $("div").queue(function () {
            $(this).removeClass("newcolor");
            $(this).dequeue();
            });
            $("div").slideUp();
            });
      <style>
            div { margin:3px; 40px; height:40px;
            position:absolute; left:0px; top:30px;
            background:green; display:none; }
            div.newcolor { background:blue; }
            </style>
            <button id="start">Start</button>
            <button id="stop">Stop</button>
            <div></div>
      $("#start").click(function () {
            $("div").show("slow");
            $("div").animate({left:'+=200'},5000);
            $("div").queue(function () {
            $(this).addClass("newcolor");
            $(this).dequeue();
            });
            $("div").animate({left:'-=200'},1500);
            $("div").queue(function () {
            $(this).removeClass("newcolor");
            $(this).dequeue();
            });
            $("div").slideUp();
            });
              $("#stop").click(function () {
            $("div").queue("fx", []);
            $("div").stop();
            });

    由于div节点产生动画效果,每条动画就调用一个jQuery.data方法,将每条动作保存在jQuery.cache中,就形成了缓存队列。至于div节点产生动画效果如何调用jQuery.data方法会在以后的章节中介绍。

    请看第一行的例子,可以看到这里包含7条动作效果,也就是在还没有执行它们以前,如果调用 var n = $("div").queue("fx"); 返回一个队列对象n,查看该对象的长度,发现队列长度为7,而每执行完一条动作,队列长度就会减1。

    再看第二行的例子,queue的第一个参数为一个函数,当执行完这个自定义函数后,要继续执行队列,这要调用dequeue方法。

    再看第三行的例子,queue的第二个参数为一个数组,实际上它可以是一个新队列或者现有队列去替代当前队列,其中新队列或者现有队列的值和queue(callback)相同。

    现在看看queue的具体实现:

    queue: function(type, data){
        
    if ( typeof type !== "string" ) {
            data 
    = type;
            type 
    = "fx";
        } 

        
    if ( data === undefined )
        {
            
    return jQuery.queue( this[0], type );} 

        
    return this.each(function(){
            
    var queue = jQuery.queue( this, type, data );
             
    if( type == "fx" && queue.length == 1 )
                queue[
    0].call(this);
        });
    }

    其中 if(typeof type !== "string") { data = type; type = "fx"; } 可以得出fx为默认的队列名称。继续查看jQuery.queue方法:

    queue: function( elem, type, data ) {
        
    if ( elem ){ 

            type 
    = (type || "fx"+ "queue"

            
    var q = jQuery.data( elem, type ); 

            
    if ( !|| jQuery.isArray(data) )
                q 
    = jQuery.data( elem, type, jQuery.makeArray(data) );
            
    else if( data )
                q.push( data ); 

        }
        
    return q;
    }

    归根结底最后通过jQuery.data从jQuery.cache对象获得数据。jQuery.isArray(data) 判断是否是新队列或者现有队列数组。


    4. 【dequeue([name])

    从队列最前端移除一个队列函数,并执行它。

    dequeue的具体实现为:

    dequeue: function(type){
        
    return this.each(function(){
            jQuery.dequeue( 
    this, type );
        });
    }

    然后查看jQuery.dequeue方法的具体实现如下:

    dequeue: function( elem, type ){
        
    var queue = jQuery.queue( elem, type ),
            fn 
    = queue.shift();
        
    if!type || type === "fx" )
            fn 
    = queue[0];
        
    if( fn !== undefined )
            fn.call(elem);
    }

    可以发现最后通过 fn=queue.shift();或者fn=queue[0]得到队列的第一个元素,然后fn.call(elem);去执行它。


    好了,jQuery对象访问和数据缓存的原理机制就是这样的。

  • 相关阅读:
    kafka 启动停止
    kafka消息长度限制
    python给回调函数传参数
    Promise封装setTimeout
    Twisted 基础
    kafka-eagle
    go安装
    python asyncio
    ajv参数验证
    sequlizejs学习笔记整理
  • 原文地址:https://www.cnblogs.com/lzhdim/p/1390614.html
Copyright © 2011-2022 走看看