WEB开发网
开发学院网页设计JavaScript jQuery.API源码深入剖析以及应用实现(2) - jQuer... 阅读

jQuery.API源码深入剖析以及应用实现(2) - jQuery对象访问和数据缓存

 2010-09-14 13:40:09 来源:WEB开发网   
核心提示:前言上篇主要介绍JQuery的核心函数的原理机制,这篇将开始介绍jQuery对象访问和数据缓存原理,jQuery.API源码深入剖析以及应用实现(2) - jQuery对象访问和数据缓存,主要内容包括:分析一、jQuery对象访问1. 【each(callback)】例子:HTML代码jQuery代码运行结果<i

前言

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

jQuery.API源码深入剖析以及应用实现(2) - jQuery对象访问和数据缓存jQuery.API源码深入剖析以及应用实现(2) - jQuery对象访问和数据缓存

分析

一、jQuery对象访问

1. 【each(callback)】

例子:

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

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

[ <img src=http://tech.ddvip.com/2009-02/"test0.jpg" />, <img src=http://tech.ddvip.com/2009-02/"test1.jpg" /> ]
<img/><img/>

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

切换样式example

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

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

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

//args作为内部成员的调用来使用
each:function(object,callback,args){
  varname,i=0,length=object.length; //当object为jQuery对象时,length非空
  if(args){
    if(length===undefined){
      for(nameinobject)
        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(nameinobject)
        if(callback.call(object[name],name,object[name])===false)
          break;
    }else
      for(varvalue=object[0];
        i<length&&callback.call(value,i,value)!==false;value=object[++i]){}
  }
  returnobject;
}

现在我们关注下 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(){
  returnthis.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){
  returnnum===undefined?
    //当num为undefined时,返回所有匹配的DOM元素集合
    jQuery.makeArray(this):
    //当num不为undefined时,返回第num+1个匹配的DOM元素
    this[num];
}

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

makeArray:function(array){
  varret=[];
  if(array!=null){
    vari=array.length;
    //Thewindow,strings(andfunctions)alsohave'length'
    if(i==null||typeofarray==="string"||jQuery.isFunction(array)||array.setInterval)
      ret[0]=array;
    else
      while(i)
        ret[--i]=array[i];
  }
  returnret;
}

可以看出这里的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){
  returnjQuery.inArray(
    //如果elem为一个jQuery对象,那么得到的是第一个匹配的DOM元素
    elem&&elem.jquery?elem[0]:elem
  ,this);
}

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

inArray:function(elem,array){
  for(vari=0,length=array.length;i<length;i++)
  //Use===becauseonIE,window==document
    if(array[i]===elem)
      returni;
  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){
    varparts=key.split(".");
    parts[1]=parts[1]?"."+parts[1]:"";
    if(value===undefined){
      vardata=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);
      if(data===undefined&&this.length)
        data=jQuery.data(this[0],key);
      returndata===undefined&&parts[1]?
        this.data(parts[0]):
        data;
    }else
      returnthis.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){
        jQuery.data(this,key,value);
      });
  },
  removeData:function(key){
    returnthis.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方法的具体实现以及相关其他代码:

functionnow(){
  return+newDate;
}
varexpando="jQuery"+now(),uuid=0,windowData={};
jQuery.extend({
  cache:{},
  data:function(elem,name,data){
    elem=elem==window?
      windowData:
      elem;
    varid=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;
    returnname?
      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){
  returnthis.each(function(){
    jQuery.removeData(this,key);
  });
}

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

removeData:function(elem,name){
  elem=elem==window?
    windowData:
    elem;
  varid=elem[expando];
  if(name){
    //elem是否存在元素cache
    if(jQuery.cache[id]){
      //移除一个具有特定键的元素数据
      deletejQuery.cache[id][name];
      //将键值置空,准备移除元素cache
      name="";
      for(nameinjQuery.cache[id])
        break;
      if(!name)
        jQuery.removeData(elem);
    }
  }else{
    //Cleanuptheelementexpando
    try{
      deleteelem[expando];
    }catch(e){
      //IEhastroubledirectlyremovingtheexpando
      //butit'sokwithusingremoveAttribute
      if(elem.removeAttribute)
        elem.removeAttribute(expando);
    }
    //完全移除元素cache
    deletejQuery.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; width: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; width: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; width: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(typeoftype!=="string"){
    data=type;
    type="fx";
  }
  if(data===undefined)
  {
    returnjQuery.queue(this[0],type);}
  returnthis.each(function(){
    varqueue=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";
    varq=jQuery.data(elem,type);
    if(!q||jQuery.isArray(data))
      q=jQuery.data(elem,type,jQuery.makeArray(data));
    elseif(data)
      q.push(data);
  }
  returnq;
}

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

4. 【dequeue([name])】

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

dequeue的具体实现为:

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

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

dequeue:function(elem,type){
  varqueue=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对象访问和数据缓存的原理机制就是这样的。

系列文章:

jQuery.API源码深入剖析以及应用实现(1) - 核心函数篇

jQuery.API源码深入剖析以及应用实现(2) - jQuery对象访问和数据缓存

Tags:jQuery API 源码

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接