`

jquery1.43源码分析之核心部分

    博客分类:
  • AJAX
阅读更多
jquery核心


一  构造jquery.

相对于其它库里传统的构造对象方法. jquery提供了一种截然不同的方法. 它选择创造一个全新的奇异世界.

首先所有的jquery代码被一个自动执行的闭包包裹起来, 只在后面暴露$和jQuery这2个变量给外界 尽量避开变量冲突.
Java代码

   1. (function(window,  undefined){ 
   2. ….. 
   3. })(window) 

(function(window,  undefined){
…..
})(window)


window和undefined都是为了减少变量查找所经过的scope. 当window通过传递给闭包内部之后, 在闭包内部使用它的时候,
可以把它当成一个局部变量, 显然比原先在window scope下查找的时候要快一些.
undefined也是同样的道理, 其实这个undefined并不是javascript数据类型六君子之一的undefined, 而是一个普普通通的变量名.
只是因为没给它传递值. 它的值就是undefined.  undefined并不是javascript的保留字.

然后是一个套子套住jquery的构造方法
Java代码

   1. var jQuery = (function(){   --------------------jq1 
   2.     var jQuery = function( selector, context ){    ---------------jq2 
   3.         …….. 
   4. }; 
   5.     return (window.jQuery = window.$ = jQuery); 
   6. })() 

var jQuery = (function(){   --------------------jq1
var jQuery = function( selector, context ){    ---------------jq2
……..
};
return (window.jQuery = window.$ = jQuery);
})()



首先定义jq1, 这个jQuery最终在return  (window.jQuery = window.$ = jQuery)的时候会变成
window下面的变量供外界使用.
而jq2供jquery内部执行的时候调用. 最终作为jq1的引用返回.
return (window.jQuery = window.$ = jQuery);这句话等价于
Java代码

   1. window.jQuery = window.$ = jQuery; 
   2. return window. jQuery. 

window.jQuery = window.$ = jQuery;
return window. jQuery.




现在来看看jquery对象是怎么被创建出来的. jquery作为一个独立特行的库,  它产生jquery对象时并不需要用new 操作符..
它宁愿选择这种方式, 比如要产生一个构造函数Man的对象.
Java代码

   1. function Man(name){ 
   2.     this.name = name;    
   3. } 
   4.  
   5. function People(name){ 
   6.     return new Man(name); 
   7. } 
   8.  
   9. var pe = People("zengtan"); 
  10. alert (pe.name); 

function Man(name){
this.name = name;
}

function People(name){
return new Man(name);
}

var pe = People("zengtan");
alert (pe.name);


同样真正作为jQuery对象的构造方法的并不是
function (selector, context){
}
而是jQuery.fn.init.
Java代码

   1. var jQuery  =  function( selector, context ) {           
   2.         return new jQuery.fn.init( selector, context ); 
   3.     } 

var jQuery  =  function( selector, context ) {
return new jQuery.fn.init( selector, context );
}


jQuery.fn就是jQuery.prototype. 见源码102行.
jQuery.fn = jQuery.prototype = {}

init是挂在jQuery.prototype上的属性.
当jQuery(‘div’)的时候, 实际上转交给了jQuery.fn.init构造函数来生成对象.
当然我们想用new jQuery来生成jquery对象也可以. 跟直接用jQuery()没区别.
因为构造函数一定会返回一个对象.如果显示指定了返回某个对象.就会返回那个对象,
否则才会返回this对象.
好比说, 有只母鸡被你强迫下一个蛋, 它会先看窝里有没有别人的蛋, 如果没有,才会自己努力下一个.
这里显然返回的是jQuery.fn.init的对象.

也许现在你开始回忆制作jquery插件时, 明明是给jQuery.prototype添加方法.
这里返回的又是jQuery.prototype.init的对象.
原来在源码333行,  jQuery.prototype.init.prototype = jQuery. prototype;
现在很容易看明白. 给jQuery.prototype添加方法就等于给jQuery. prototype.init.prototype添加方法了.
JQuery api里的方法大部分都是通过jQuery.prototype扩展上去的, 除此之外. 我们还要给jquery对象加上索引. 给集合添加length属性,让他们更像一个数组里的元素.

搞明白这些, 再来看jQuery. prototype.init这个方法里究竟是怎样生产jquery对象的.
我们可以把jQuery. prototype.init想象成一个火腿肠加工器. 只要你放了正确的原料进去,
它就可以把原料变成火腿肠生产出来.如果你不小心放错了原料.它也会帮你变成火腿肠. 不过只有塑料包装, 里面没有火腿.
当然这个加工器里面的构造是很复杂的, 它需要判断材料种类, 数量等等.
一般这个材料主要为这4种情况
1 dom节点
2 字符串
3 函数
4 数组
5 其他元素

一 jQuery构造方法
jQuery的构造方法会生成一组jquery对象的集合.具体关于init方法的分析, 还是留在选择器部分说吧.

二 jQuery对象访问

jquery构造完对象之后, 会提供一些方法访问这些对象.

1  jQuery.prototype.size
集合内元素的数量
就是通过this.length得到.

2  jQuery.prototype.get
按照索引取得集合内某个元素, 返回包装前的原始对象
Java代码

   1. get: function( num ) { 
   2.         return num == null ?  
   3.  //如果参数为null或者undefiend. 注意是==. 
   4.             this.toArray() :  //如果不传参数, 集合内的元素全部转化为一个数组(1) 
   5.             ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );  
   6. //调用silce方法截取某一个.可以传入负数并且返回的是包装前的对象. 
   7.     } 
   8.  
   9. (1) 看看这里的this.toArray函数. 
  10. toArray: function() { 
  11.         return slice.call( this, 0 ); 
  12.     } 

get: function( num ) {
return num == null ?
//如果参数为null或者undefiend. 注意是==.
this.toArray() :  //如果不传参数, 集合内的元素全部转化为一个数组(1)
( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
//调用silce方法截取某一个.可以传入负数并且返回的是包装前的对象.
}

(1) 看看这里的this.toArray函数.
toArray: function() {
return slice.call( this, 0 );
}


很简单, 就是让当前jquery对象冒充Array的对象, 调用Array.prototype.slice进行截断. 返回的是一个数组.
至于为什么可以像这样使用对象冒充. 我们抽个地方来好好讨论一下.
其实如果查看v8之类开源引擎的源码就知道(当然也可以在ecma里挣扎一番). 要调用Array原型链上的方法. 通常这个对象满足2个条件就可以了.
1, 本身可以存取属性.
2, length属性不能是只读(可以没有length属性).
由于Array.prototype.slice方法太长了. 我拿Array.prototype.push方法举例. 在V8的src目录下的array.js可以找到这些方法.
比如push
Java代码

   1. function ArrayPush() { 
   2.   var n = ToUint32(this.length); 
   3.  /*
   4.     对象/数组本身的length. 如果为null或者undefined, 会在ToUint32中转化为0. 所以
   5.     即使没有length属性,也会给一个默认的0值.
   6.  */ 
   7.   var m = %_ArgumentsLength();    //参数的length. 
   8.   for (var i = 0; i < m; i++) { 
   9.     this[i+n] = %_Arguments(i);   //复制属性 
  10.   } 
  11.   this.length = n + m;            //重设length. 
  12.   return this.length; 
  13. } 

function ArrayPush() {
  var n = ToUint32(this.length);
/*
    对象/数组本身的length. 如果为null或者undefined, 会在ToUint32中转化为0. 所以
    即使没有length属性,也会给一个默认的0值.
*/
  var m = %_ArgumentsLength();    //参数的length.
  for (var i = 0; i < m; i++) {
    this[i+n] = %_Arguments(i);   //复制属性
  }
  this.length = n + m;            //重设length.
  return this.length;
}


可以看到push操作的核心就是复制属性和重设长度.
jquery对象完全可以满足这2个条件. 同样的道理 一个对象字面量{}也可以.
而string类型的不可以, 因为不能在string上存取属性. function对象虽然可以存取属性, 也有length属性.
不过它的length属性比较特殊, 表示形参的个数, 是一个只读属性, 源码中的this.length = n + m这一句不起作用, 所以function对象也不行. 同理window对象也不行.
上面的slice.call也是这个原理, 虽然slice方法的实现更复杂一点.

明白了这个,我们可以解释很多奇怪的问题.比如:
Java代码

   1. var a = {}; 
   2. a[0] = 1; 
   3. a[1] = 2; 
   4. Array.prototype.push(a, 3); 
   5. alert (a.length)   // a没有length属性, 默认的给0值, 所以结果为1 . 

var a = {};
a[0] = 1;
a[1] = 2;
Array.prototype.push(a, 3);
alert (a.length)   // a没有length属性, 默认的给0值, 所以结果为1 .


如果在push操作之前添加一句 a.length = 2;
再进行push操作后, a.length就为3了.



3 jQuery.prototype.index
搜索匹配的元素,并返回相应元素的索引值,从0开始计数。
如果不给 .index() 方法传递参数,那么返回值就是这个jQuery对象集合中第一个元素相对于其同辈元素的位置。
如果参数是一组DOM元素或者jQuery对象,那么返回值就是传递的元素相对于原先集合的位置。
如果参数是一个选择器,那么返回值就是原先元素相对于选择器匹配元素中的位置。如果找不到匹配的元素,则返回-1。
没有什么特别需要解释的, 直接看代码.
Java代码

   1. index: function( elem ) { 
   2.         if ( !elem || typeof elem === "string" ) {    
   3.         //如果没有参数, 或者参数是选择器. 
   4.             return jQuery.inArray( this[0], 
   5.                 elem ? jQuery( elem ) : this.parent().children() ); 
   6.         //如果有参数(选择器), 则查找元素本身在这些选择器组成的jq对象集合中的位置 
   7.         //如果没有参数, 查找元素本身在它的兄弟节点之间的位置. this.parent().children()可以取得自己和全部兄弟节点. 
   8. } 
   9.         return jQuery.inArray( 
  10.             //参数是一个对象 
  11.             elem.jquery ? elem[0] : elem, this ); 
  12.             //如果是jquery对象, 取得它的原始节点. 再查找在当前集合中的位置    
  13. } 

index: function( elem ) {
if ( !elem || typeof elem === "string" ) {  
//如果没有参数, 或者参数是选择器.
return jQuery.inArray( this[0],
elem ? jQuery( elem ) : this.parent().children() );
//如果有参数(选择器), 则查找元素本身在这些选择器组成的jq对象集合中的位置
//如果没有参数, 查找元素本身在它的兄弟节点之间的位置. this.parent().children()可以取得自己和全部兄弟节点.
}
return jQuery.inArray(
//参数是一个对象
elem.jquery ? elem[0] : elem, this );
//如果是jquery对象, 取得它的原始节点. 再查找在当前集合中的位置
}


顾名思义jQuery.inArray就是判断数组里有没有某个元素.当然这里的数组也包括伪数组.
这个方法虽然实现起来很简单, 关于inArray这个名字在jquery的官方论坛却有颇多争议.
很多人认为它应该返回true或者false, 而不是索引的位置.
john resig只是说暂时还不准备修改这个方法.

有些浏览器还不支持Array.prototype.indexOf方法. 所以首先在源码的851行, 有这样一段代码.
Java代码

   1. if ( Array.prototype.indexOf ) { 
   2.     jQuery.inArray = function( elem, array ) { 
   3.         return indexOf.call( array, elem ); 
   4.     }; 
   5. } 

if ( Array.prototype.indexOf ) {
jQuery.inArray = function( elem, array ) {
return indexOf.call( array, elem );
};
}


如果支持Array.prototype.indexOf. 则重写jQuery.inArray, 直接用Array.prototype.indexOf.call(array, elem );
在页面加载的时候就重写这个方法. 也避免了在函数里反复判断造成的浪费.
然后
Java代码

   1. inArray: function( elem, array ) { 
   2.         if ( array.indexOf ) { 
   3. //确认indexOf方法存在.或防止indexOf方法被改写. 
   4.             return array.indexOf( elem ); 
   5.         } 
   6.         for ( var i = 0, length = array.length; i < length; i++ ) { 
   7.             //否则遍历数组, 返回正确的索引. 
   8. if ( array[ i ] === elem ) { 
   9.                 return i; 
  10.             } 
  11.         } 
  12.         return -1;  //如果数组里没有这个元素, 返回-1. 
  13.     } 

inArray: function( elem, array ) {
if ( array.indexOf ) {
//确认indexOf方法存在.或防止indexOf方法被改写.
return array.indexOf( elem );
}
for ( var i = 0, length = array.length; i < length; i++ ) {
//否则遍历数组, 返回正确的索引.
if ( array[ i ] === elem ) {
return i;
}
}
return -1;  //如果数组里没有这个元素, 返回-1.
}


三 数据缓存

jQuery.data

在实际应用中, 我们经常需要往节点中缓存一些数据. 这些数据往往和dom元素紧密相关. dom节点也是对象, 所以我们可以直接扩展dom节点的属性.
不过肆意污染dom节点是不良少年的行为. 我们需要一种低耦合的方式让dom和缓存数据能够联系起来.

jquery提供了一套非常巧妙的缓存办法.

我们先在jquery内部创建一个cache对象{}, 来保存缓存数据.

然后往需要进行缓存的dom节点上扩展一个值为jQuery.expando的属性, 这里是”jquery” + (new Date).getTime().

接着把每个节点的dom[jQuery.expando]的值都设为一个自增的变量id,保持全局唯一性.
这个id的值就作为cache的key用来关联dom节点和数据.
也就是说cache[id]就取到了这个节点上的所有缓存.

而每个元素的所有缓存都被放到了一个map里面,这样可以同时缓存多个数据.

比如有2个节点dom1和dom2, 它们的缓存数据在cache中的格式应该是这样
Java代码

   1. cache = { 
   2.     dom1[  jQuery.expando ]: { 
   3.         key1: value1, 
   4.         key2: value2 
   5. }, 
   6. dom2[ jQuery.expando ] { 
   7.     key3: value3, 
   8.     key4: value4 
   9. } 
  10. } 

cache = {
dom1[  jQuery.expando ]: {
key1: value1,
key2: value2
},
dom2[ jQuery.expando ] {
key3: value3,
key4: value4
}
}



jQuery.expando的值等于”jquery”+当前时间, 元素本身具有这种属性而起冲突的情况是微乎其微的.

我们在看源码之前, 先根据上面的原理来自己实现一个简单的缓存系统.以便增强理解.

先把跟data相关的所有代码都封装到一个闭包里,通过返回的接口暴露给外界.
同时为了简便,我们拆分成setData和getData两个方法.
Java代码

   1. <div id="ddd">dddddddd</div> 
   2.  
   3. <script> 
   4.  
   5. var Data = function(){ 
   6.     var cache = {}; 
   7.     var expando = "zengtan" + +new Date; 
   8.     var uuid = 1; 
   9.      
  10.     var setData = function(elem, key, value){ 
  11.             var id = elem[expando]; 
  12.             if (!id){   //第一次给元素设置缓存 
  13.                 id = ++uuid; 
  14.                 elem[expando] = id; 
  15.             } 
  16.             if (!cache[id]){   //这个元素第一次进行缓存或者缓存已被清空 
  17.                 cache[id] = {}; 
  18.             } 
  19.             cache[id][key] = value; 
  20.     }; 
  21.  
  22.     var getData = function(elem, key){ 
  23.         var id = elem[expando];  //取得cache里跟dom节点关联的key 
  24.         return cache[id] && cache[id][key] || null;  //如果缓存里没有, 返回null 
  25.     } 
  26.  
  27.     return { 
  28.         setData: setData, 
  29.         getData: getData     
  30.     } 
  31. }() 
  32.  
  33. </script> 
  34.  
  35. var div = document.getElementById("ddd"); 
  36. Data.setData(div, "name", "zengtan"); 
  37. var value = Data.getData(div, "name"); 
  38. alert (value) 

<div id="ddd">dddddddd</div>

<script>

var Data = function(){
var cache = {};
var expando = "zengtan" + +new Date;
var uuid = 1;

var setData = function(elem, key, value){
var id = elem[expando];
if (!id){   //第一次给元素设置缓存
id = ++uuid;
elem[expando] = id;
}
if (!cache[id]){   //这个元素第一次进行缓存或者缓存已被清空
cache[id] = {};
}
cache[id][key] = value;
};

var getData = function(elem, key){
var id = elem[expando];  //取得cache里跟dom节点关联的key
return cache[id] && cache[id][key] || null;  //如果缓存里没有, 返回null
}

return {
setData: setData,
getData: getData
}
}()

</script>

var div = document.getElementById("ddd");
Data.setData(div, "name", "zengtan");
var value = Data.getData(div, "name");
alert (value)


看看源码实现.

首先声明一些特殊的节点, 在它们身上存属性的时候可能会抛出异常.
Java代码

   1. noData: { 
   2.         "embed": true, 
   3.         // Ban all objects except for Flash (which handle expandos) 
   4.         "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", 
   5.         "applet": true 
   6.     } 

noData: {
"embed": true,
// Ban all objects except for Flash (which handle expandos)
"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
"applet": true
}



这个对象里的数据用在acceptData方法中, 跟1.42版本相比, 这里多了对什么flash的object的特殊处理.
总之acceptData方法就是判断节点能否添加缓存.

看具体的jQuery.data
Java代码

   1. data: function( elem, name, data ) { 
   2.     if ( !jQuery.acceptData( elem ) ) { 
   3.         return; 
   4.     } 
   5.     //noData类型 
   6.     elem = elem == window ? 
   7.         windowData : 
   8.         elem; 
   9.     //处理elem是window的情况, 如果不单独处理的话, 等于增加了一个全局变量, windowData也是一个{} 
  10.     var isNode = elem.nodeType, 
  11.     //判断是不是dom节点,由于非dom节点的缓存在继承和内存泄露上都会造成麻烦,1.43版本已经把dom节点和其他对象分开处理了. 
  12.         id = isNode ? elem[ jQuery.expando ] : null, 
  13.         cache = jQuery.cache, thisCache; 
  14.     //因为存数据的时候, 会给elem[ jQuery.expando ]设置一个全局唯一标志量. 判断id是否为undefined, 就知道已经有没有往这个元素上缓存过数据.  
  15.  
  16.     if ( isNode && !id && typeof name === "string" && data === undefined ) { 
  17.     //如果是dom节点对象, 并且现在是get方式(typeof name === "string" && data === undefined表示get方式), 又没有缓存(!id表示没有缓存). 
  18.         return; 
  19.     } 
  20.  
  21.     if ( !isNode ) { 
  22.         cache = elem; 
  23.     //如果是非dom节点对象, 取缓存里的属性就直接取元素本身属性. 
  24.     } else if ( !id ) { 
  25.         elem[ jQuery.expando ] = id = ++jQuery.uuid; 
  26.     //第一次进行缓存, 分配一个全局唯一标志id. 
  27.     } 
  28.  
  29.     if ( typeof name === "object" ) { 
  30.     //如果key是对象类型 
  31.         if ( isNode ) { 
  32.             cache[ id ] = jQuery.extend(cache[ id ], name); 
  33.     /*
  34.     把整个对象都复制到原来的缓存上, 比如
  35.          $('#ddd').data({
  36.                 "v2": "bbb",
  37.                 "v3": "ccc"
  38.             });
  39.             相当于$('#ddd').data("v2", "bbb").data("v3":"ccc");
  40.             1.42版本用的cache[ id ] = jQuery.extend(true, {}, name). 这样会清空以前的缓存.
  41.         */ 
  42.         } else { 
  43.             jQuery.extend( cache, name ); 
  44.         //如果不是dom节点. 把属性复制到元素本身. 
  45.         } 
  46.  
  47.     } else if ( isNode && !cache[ id ] ) { 
  48.         //如果cache[ id ]中没有东西, 表示这个元素第一次进行缓存或者缓存已被清空, 设置cache[ id ]为一个新的map. 
  49.         cache[ id ] = {}; 
  50.     } 
  51.  
  52.     thisCache = isNode ? cache[ id ] : cache; 
  53.  
  54.     if ( data !== undefined ) { 
  55.         /*
  56.             set操作, 也可以防止一些意外的情况下缓存被清空. 比如data未定义的情况下, 缓存操作是无效的.
  57.             var a = {};
  58.             var b;
  59.             $(a).data("c", 3);
  60.             $(a).data("c", b); b为undefined. 这句是无效的. 要移除缓存可以用removeData方法.
  61.         */ 
  62.         thisCache[ name ] = data; 
  63.         //即cache[ id ][ name ] = data, 把data设置进cache缓存对象中, 前面分配的自增id当做key来关联 
  64.     } 
  65.  
  66.     return typeof name === "string" ? thisCache[ name ] : thisCache; 
  67.     //如果key是string类型, 返回key对应的缓存, 否则返回整个元素上的缓存 
  68. } 

data: function( elem, name, data ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
//noData类型
elem = elem == window ?
windowData :
elem;
//处理elem是window的情况, 如果不单独处理的话, 等于增加了一个全局变量, windowData也是一个{}
var isNode = elem.nodeType,
//判断是不是dom节点,由于非dom节点的缓存在继承和内存泄露上都会造成麻烦,1.43版本已经把dom节点和其他对象分开处理了.
id = isNode ? elem[ jQuery.expando ] : null,
cache = jQuery.cache, thisCache;
//因为存数据的时候, 会给elem[ jQuery.expando ]设置一个全局唯一标志量. 判断id是否为undefined, 就知道已经有没有往这个元素上缓存过数据.

if ( isNode && !id && typeof name === "string" && data === undefined ) {
//如果是dom节点对象, 并且现在是get方式(typeof name === "string" && data === undefined表示get方式), 又没有缓存(!id表示没有缓存).
return;
}

if ( !isNode ) {
cache = elem;
//如果是非dom节点对象, 取缓存里的属性就直接取元素本身属性.
} else if ( !id ) {
elem[ jQuery.expando ] = id = ++jQuery.uuid;
//第一次进行缓存, 分配一个全局唯一标志id.
}

if ( typeof name === "object" ) {
//如果key是对象类型
if ( isNode ) {
cache[ id ] = jQuery.extend(cache[ id ], name);
/*
把整个对象都复制到原来的缓存上, 比如
$('#ddd').data({
"v2": "bbb",
"v3": "ccc"
});
相当于$('#ddd').data("v2", "bbb").data("v3":"ccc");
1.42版本用的cache[ id ] = jQuery.extend(true, {}, name). 这样会清空以前的缓存.
*/
} else {
jQuery.extend( cache, name );
//如果不是dom节点. 把属性复制到元素本身.
}

} else if ( isNode && !cache[ id ] ) {
//如果cache[ id ]中没有东西, 表示这个元素第一次进行缓存或者缓存已被清空, 设置cache[ id ]为一个新的map.
cache[ id ] = {};
}

thisCache = isNode ? cache[ id ] : cache;

if ( data !== undefined ) {
/*
set操作, 也可以防止一些意外的情况下缓存被清空. 比如data未定义的情况下, 缓存操作是无效的.
var a = {};
var b;
$(a).data("c", 3);
$(a).data("c", b); b为undefined. 这句是无效的. 要移除缓存可以用removeData方法.
*/
thisCache[ name ] = data;
//即cache[ id ][ name ] = data, 把data设置进cache缓存对象中, 前面分配的自增id当做key来关联
}

return typeof name === "string" ? thisCache[ name ] : thisCache;
//如果key是string类型, 返回key对应的缓存, 否则返回整个元素上的缓存
}


从新版本的源码里可以看到,  1.42版本中data方法的几个缺点已经被解决了.

当然我们用jquery缓存系统的时候, 一般调用的是prototype方法, prototype方法除了调用上面的静态方法之外. 还加入了对节点上自定义事件的处理, 留在event部分再讲.

当然, 我们还需要删除缓存的方法. 现在看看removeData的代码
Java代码

   1. Jquery.removeData 
   2.  
   3.     removeData: function( elem, name ) { 
   4.         if ( !jQuery.acceptData( elem ) ) { 
   5.             return; 
   6.         } 
   7.  
   8.         elem = elem == window ? 
   9.             windowData : 
  10.             elem; 
  11.  
  12.         var isNode = elem.nodeType, 
  13.             id = isNode ? elem[ jQuery.expando ] : elem, 
  14.             //id是该元素在cache上面的key, 如果不是节点就返回元素本身. 
  15.             cache = jQuery.cache, 
  16.             thisCache = isNode ? cache[ id ] : id; 
  17.  
  18.         if ( name ) { 
  19.             if ( thisCache ) { 
  20.                 delete thisCache[ name ]; 
  21.                 //删除key对应的缓存数据 
  22.                 if ( isNode && jQuery.isEmptyObject(thisCache) ){ 
  23.                     //如果元素是dom节点并且缓存为一个空对象.说明所有缓存的数据都已经被删掉了. 
  24.                     //那么重新调用一次removeData方法, 删掉缓存系统里已经无用的东西, 防止内存泄露, 注意现在走的是下面else分支.  
  25.                     jQuery.removeData( elem );  
  26.                 } 
  27.             } 
  28.         } else { 
  29.             if ( isNode && jQuery.support.deleteExpando ) { 
  30.                 //如果支持delete, 见特性检测部分. 
  31.                 delete elem[ jQuery.expando ]; 
  32.                 //删掉元素的jQuery.expando属性 
  33.  
  34.             } else if ( elem.removeAttribute ) { 
  35.                 //如果支持removeAttribute 
  36.                 elem.removeAttribute( jQuery.expando ); 
  37.  
  38.             } else if ( isNode ) { 
  39.                 delete cache[ id ]; 
  40.                 //如果是dom节点, 全局缓存里删除以这个id为key的对象. 
  41.             } else { 
  42.                 for ( var n in elem ) { 
  43.                     //如果是其它对象, 删除对象的所有属性. 防止内存泄露. 
  44.                     delete elem[ n ]; 
  45.                 } 
  46.             } 
  47.         } 
  48.     } 

Jquery.removeData

removeData: function( elem, name ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}

elem = elem == window ?
windowData :
elem;

var isNode = elem.nodeType,
id = isNode ? elem[ jQuery.expando ] : elem,
//id是该元素在cache上面的key, 如果不是节点就返回元素本身.
cache = jQuery.cache,
thisCache = isNode ? cache[ id ] : id;

if ( name ) {
if ( thisCache ) {
delete thisCache[ name ];
//删除key对应的缓存数据
if ( isNode && jQuery.isEmptyObject(thisCache) ){
//如果元素是dom节点并且缓存为一个空对象.说明所有缓存的数据都已经被删掉了.
//那么重新调用一次removeData方法, 删掉缓存系统里已经无用的东西, 防止内存泄露, 注意现在走的是下面else分支.
jQuery.removeData( elem );
}
}
} else {
if ( isNode && jQuery.support.deleteExpando ) {
//如果支持delete, 见特性检测部分.
delete elem[ jQuery.expando ];
//删掉元素的jQuery.expando属性

} else if ( elem.removeAttribute ) {
//如果支持removeAttribute
elem.removeAttribute( jQuery.expando );

} else if ( isNode ) {
delete cache[ id ];
//如果是dom节点, 全局缓存里删除以这个id为key的对象.
} else {
for ( var n in elem ) {
//如果是其它对象, 删除对象的所有属性. 防止内存泄露.
delete elem[ n ];
}
}
}
}



四 队列控制
队列控制也是jquery中很有特点的一个功能. 可以用来管理动画或者事件等的执行顺序. queue和dequeue主要为动画服务.
比如在jquery的动画里, 因为javascript的单线程异步机制, 如果要管理一批动画的执行顺序, 而不是让它们一起在屏幕上飞舞.
一般我们是一个一个的把下个动画写在上个动画的回调函数中, 意味着如果要让十个动画按次序执行. 至少要写9个回调函数.
好吧我承认我做过这样的事, 直接导致我的视力从5.2变为5.1.

现在有了队列机制, 可以把动画都放到队列中依次执行.究竟怎样把动画填充进队列.用的是queue方法.
不过queue非常先进的是.把动画push进队列之后,还会自动去执行队列里的第一个函数.
队列机制里面的另外一个重要方法是dequeue, 意为取出队列的第一个函数并执行.此时队列里面的函数个数变为N-1.

看个例子.比如我要让2个div以动画效果交替隐藏和显示.同时只能有一个div在进行动画.
Java代码

   1. <style type="text/css">  
   2. #div1 {background:#aaa;width:188px;height:188px;position:absolute;top:10px;left: 110px;} 
   3. #div2 {background:#aaa;width:188px;height:188px;position:absolute;top:310px;left: 110px;} 
   4. </style> 
   5.  
   6. <body>  
   7. <div id="div1">我是一个div</div> 
   8. <div id="div2">我是另一个div</div> 
   9. </body> 
  10.  
  11. 用queue可以这样做. 
  12. <script type="text/javascript"> 
  13. $(document).queue([ 
  14.     function(fn){ 
  15.         $("#div1").hide(1000, fn);  
  16.       //fn === $(document).dequeue; 
  17.     }, 
  18.     function(fn){ 
  19.         $("#div2").hide(1000, fn); 
  20.     }, 
  21.     function(fn){ 
  22.         $("#div1").show(1000, fn); 
  23.     }, 
  24.     function(fn){ 
  25.         $("#div2").show(1000, fn); 
  26.     } 
  27. ]) 
  28. </script> 

<style type="text/css">
#div1 {background:#aaa;width:188px;height:188px;position:absolute;top:10px;left: 110px;}
#div2 {background:#aaa;width:188px;height:188px;position:absolute;top:310px;left: 110px;}
</style>

<body>
<div id="div1">我是一个div</div>
<div id="div2">我是另一个div</div>
</body>

用queue可以这样做.
<script type="text/javascript">
$(document).queue([
function(fn){
$("#div1").hide(1000, fn);
      //fn === $(document).dequeue;
},
function(fn){
$("#div2").hide(1000, fn);
},
function(fn){
$("#div1").show(1000, fn);
},
function(fn){
$("#div2").show(1000, fn);
}
])
</script>


首先我们需要一个载体, 来保存这个队列, 这里选择了document. 其实选什么节点都一样, 保存队列其实也是一个jQuery.data操作.
然后queue的参数是一个数组. 里面的函数就是队列依次执行的函数. 前面讲到, queue方法会自动把队列里的第一个函数取出来执行.
意味着这些代码写完后, div1已经开始渐渐隐藏了. 隐藏完毕后, 如果要让后面的动画继续执行, 还要用$(document).dequeue()继续取出并执行现在队列里的第一个函数.
当然这个操作是放在第一个动画的回调函数里, 以此类推, 第二个.dequeue()要放在第二个动画的回调函数里.
我们看到这里没有用$(document).dequeue(). 因为这句代码太长. 注意队列函数里有一个参数fn, fn是dequeue方法内部传给此函数的, 就是$(document).dequeue().

在看源码之前, 先自己来想想这个功能应该怎么实现.
首先我们需要一个数组, 里面存放那些动画. 然后需要2个方法, set和get.可以存取动画.
可能我们还需要一个变量来模拟线程锁, 保证队列里的函数不会同时被执行.
最后我们要把这个数组存入dom的缓存中, 方便随时存取和删除.

看源码, 先是prototype方法.
Java代码

   1. jQuery.prototype.queue = function( type, data ) { 
   2.         if ( typeof type !== "string" ) { 
   3.             data = type; 
   4.             type = "fx";   
   5. //修正type, 默认为表示jquery动画的fx, 如果不为"fx", 即为自己的自定义动画, 一般我们用"fx"就足够了. 
   6.         } 
   7.         if ( data === undefined ) {  
   8.             return jQuery.queue( this[0], type );   
   9. //get队列. 不论集合中是单个或者多个元素, 总是返回第一个元素的队列 
  10.         } 
  11.         return this.each(function( i, elem ) {   
  12. //set队列, 遍历集合内所有元素 
  13.             var queue = jQuery.queue( this, type, data );   
  14. //set并返回队列 
  15.             if ( type === "fx" && queue[0] !== "inprogress" ) { 
  16.          //防止在执行函数的时候, 这里又进行dequeue操作, 这样会同时执行2个函数, 队列就不受控制了. 
  17.                 jQuery.dequeue( this, type );     
  18. //如果队列没有被锁住, 即此时没有在执行dequeue. 移出队列里第一个函数并执行它. 
  19. //毕竟queue的主要任务是添加队列, 执行函数的操作上, 只能当二等公民. 
  20.             } 
  21.         }); 
  22.     } 
  23. 看看jQuery.queue这个静态方法. 
  24.     jQuery.queue = function( elem, type, data ) { 
  25.         if ( !elem ) { 
  26.             return; 
  27.         } 
  28.         type = (type || "fx") + "queue"; 
  29.         var q = jQuery.data( elem, type ); 
  30.         //取得元素缓存上面的队列 
  31.         if ( !data ) {   
  32.         //如果当data为空, 只是查询queue. 那么返回队列或者[] 
  33. return q || []; 
  34.         } 
  35.         if ( !q || jQuery.isArray(data) ) { 
  36.             q = jQuery.data( elem, type, jQuery.makeArray(data) );  
  37.         //否则是set操作,注意!q || jQuery.isArray(data)这句代码的含义,  
  38. //如果q为null, 说明缓存上没有队列. 此时哪怕队列里只有一个函数, 也把它转成数组, 保证队列是数组形式 
  39.         //如果q不为null, 说明缓存上已经存在队列, 但是如果现在被添加的函数是一个数组的话, 以现在的数组来代替原来的队列, 即把原来的队列清空了. 
  40.         // clearQueue函数就是用的这一句代码执行清空队列操作.    
  41. } else { 
  42.             q.push( data ); 
  43.         //缓存上有队列并且只添加一个函数 
  44.         } 
  45.         return q; 
  46.     } 
  47.  
  48. 再看dequeue, dequeue的原型方法什么也没做, 直接把参数交给jQuery.dequeue来处理. 
  49.  
  50.     dequeue: function( elem, type ) { 
  51.         type = type || "fx"; 
  52.         var queue = jQuery.queue( elem, type ), fn = queue.shift(); 
  53.         //取得队列, 和队列里的第一个元素, 这里直接给shift掉了, 先斩后奏. 
  54.         if ( fn === "inprogress" ) { 
  55.         //如果fn===” inprogress”, 说明杀错了人, 我要找的是函数 
  56.         fn = queue.shift(); 
  57.         //继续找下一个, 下一个肯定是函数了. 
  58.         } 
  59.         if ( fn ) { 
  60.             if ( type === "fx" ) { 
  61.                 queue.unshift("inprogress"); 
  62.         //函数执行前, 在queue数组的最前面添加一个进程锁, 复活刚才杀错的人.  
  63. } 
  64.  
  65.             fn.call(elem, function() { 
  66.         //执行函数, 参数就是前面的fn 
  67.                 jQuery.dequeue(elem, type); 
  68.             }); 
  69.         } 
  70.     } 
  71. }) 

jQuery.prototype.queue = function( type, data ) {
if ( typeof type !== "string" ) {
data = type;
type = "fx"; 
//修正type, 默认为表示jquery动画的fx, 如果不为"fx", 即为自己的自定义动画, 一般我们用"fx"就足够了.
}
if ( data === undefined ) {
return jQuery.queue( this[0], type ); 
//get队列. 不论集合中是单个或者多个元素, 总是返回第一个元素的队列
}
return this.each(function( i, elem ) { 
//set队列, 遍历集合内所有元素
var queue = jQuery.queue( this, type, data ); 
//set并返回队列
if ( type === "fx" && queue[0] !== "inprogress" ) {
//防止在执行函数的时候, 这里又进行dequeue操作, 这样会同时执行2个函数, 队列就不受控制了.
jQuery.dequeue( this, type );   
//如果队列没有被锁住, 即此时没有在执行dequeue. 移出队列里第一个函数并执行它.
//毕竟queue的主要任务是添加队列, 执行函数的操作上, 只能当二等公民.
}
});
}
看看jQuery.queue这个静态方法.
jQuery.queue = function( elem, type, data ) {
if ( !elem ) {
return;
}
type = (type || "fx") + "queue";
var q = jQuery.data( elem, type );
//取得元素缓存上面的队列
if ( !data ) { 
//如果当data为空, 只是查询queue. 那么返回队列或者[]
return q || [];
}
if ( !q || jQuery.isArray(data) ) {
q = jQuery.data( elem, type, jQuery.makeArray(data) );
//否则是set操作,注意!q || jQuery.isArray(data)这句代码的含义,
//如果q为null, 说明缓存上没有队列. 此时哪怕队列里只有一个函数, 也把它转成数组, 保证队列是数组形式
//如果q不为null, 说明缓存上已经存在队列, 但是如果现在被添加的函数是一个数组的话, 以现在的数组来代替原来的队列, 即把原来的队列清空了.
// clearQueue函数就是用的这一句代码执行清空队列操作.
} else {
q.push( data );
//缓存上有队列并且只添加一个函数
}
return q;
}

再看dequeue, dequeue的原型方法什么也没做, 直接把参数交给jQuery.dequeue来处理.

dequeue: function( elem, type ) {
type = type || "fx";
var queue = jQuery.queue( elem, type ), fn = queue.shift();
//取得队列, 和队列里的第一个元素, 这里直接给shift掉了, 先斩后奏.
if ( fn === "inprogress" ) {
//如果fn===” inprogress”, 说明杀错了人, 我要找的是函数
fn = queue.shift();
//继续找下一个, 下一个肯定是函数了.
}
if ( fn ) {
if ( type === "fx" ) {
queue.unshift("inprogress");
//函数执行前, 在queue数组的最前面添加一个进程锁, 复活刚才杀错的人.
}

fn.call(elem, function() {
//执行函数, 参数就是前面的fn
jQuery.dequeue(elem, type);
});
}
}
})


“inprogress”进程锁是这样工作的:
如果是dequeue操作, 去掉锁, 执行队列里的函数, 同时给队列加上锁. 如果是queue操作, 要看锁的状态, 如果被锁上了, 就只执行队列的添加操作. 不再调用dequeue.
其实dequeue和queue都可以执行队列里的第一个函数.queue操作添加完队列之后, 会调用dequeue方法去执行函数.
但是用dequeue执行函数的时候, 这时候如果又用queue触发dequeue的话, 很可能同时有2个函数在执行. 队列就失去一大半意义了(还是可以保证顺序, 但是2个动画会同时执行).
不过这个锁只能保证在dequeue的时候, 不被queue操作意外的破坏队列.
如果人为的同时用2个dequeue, 还是会破坏动画效果的. 所以要把fn写在回调函数里.

清空队列
Java代码

   1. clearQueue: function( type ) { 
   2.         return this.queue( type || "fx", [] ); 
   3.     } 

clearQueue: function( type ) {
return this.queue( type || "fx", [] );
}


原理上面已经提到过了, 就是这一句

Java代码

   1. if ( !q || jQuery.isArray(data) ) { 
   2.             q = jQuery.data( elem, type, jQuery.makeArray(data) );  
   3. } 

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


用一个空数组代替了原来的队列.

五  多库共存
jQuery.noConflict
将变量$的控制权让渡给上一个实现它的那个库.
可能很多人都被多库共存时的$变量问题困扰过. 比如你先引入了prototype库,然后又引入了jquery.因为jquery的$会覆盖prototype的$.
现在想调用prototype的$怎么办呢, noConflict方法就派上用场了.
Jquery代码里最开始就有一句_$ = window.$ , 在加载的时候就用_$来引用原来的$ (比如现在就是prototype库里的$).
Java代码

   1. jQuery.extend({    
   2.     noConflict: function( deep ) { 
   3.         window.$ = _$;    
   4. //window.$归还给原来的库. 要回归jquery再用window.$ = jQuery就可以了. 
   5.         if ( deep ) {     
   6.             window.jQuery = _jQuery;    
   7. //如果deep为true, jQuery这个变量也可以归还, 不过一般不需要也不推荐这么做. 
   8.         } 
   9.         return jQuery; 
  10.     } 

jQuery.extend({  
noConflict: function( deep ) {
window.$ = _$;  
//window.$归还给原来的库. 要回归jquery再用window.$ = jQuery就可以了.
if ( deep ) {   
window.jQuery = _jQuery;  
//如果deep为true, jQuery这个变量也可以归还, 不过一般不需要也不推荐这么做.
}
return jQuery;
}


继承和拷贝
jQuery.prototype.extend和jQuery.extend
扩展 jQuery 元素集来提供新的方法, 或者把一个对象的属性拷贝到另外一个对象上.
通常javascript的继承实现有4种方式.
1 构造继承
2 prototype继承
3 属性拷贝
4 实例继承(这个有点特殊, 主要用于继承内置对象)

这4种继承都有各自的优点和缺陷.
构造继承查找属性快.但无法继承原型链上的方法,而且每次都要为属性复制一份新的副本进行储存
原型继承在性能方面好一些,可以共享原型链.但查找属性慢,因为可能要遍历N条原型链才能找到某个属性.而且原型链太多,会使得结构越加混乱.并且会丢失对象的constructor属性
(对象的constructor总是指向原型链最上层的构造器)
属性拷贝非常灵活,但明显效率偏低. 而且仅仅只是模拟继承.
实例继承主要用来继承Array, Date等内置对象, 用的较少.

jquery里采用的是属性拷贝.其实用哪种继承是个见仁见智的问题. 也有其它一些库就是用的原型继承.

前面也可以注意到, jquery只在构造方法或者原型链上定义了少量的核心方法. 其它功能块都是通过extend函数拷贝上去.按需定制.
好比开发商交给我们房子的时候, 只安装了水电暖气等基本设施.电视机洗衣机显然是自己去买比较好.

属性拷贝原理就是遍历第二个对象, 然后分别把键和值填入第一个对象中.类似于
Java代码

   1. for (var i in obj2){ 
   2.     Obj1[i] = obj2[i] 
   3. } 

for (var i in obj2){
Obj1[i] = obj2[i]
}


不幸的是jquery里面的做法复杂的多,不仅可以把多个参数的属性都复制到一个对象中,还能实现深度继承.
深度继承是指如果被继承对象和继承对象都有同名属性a, 而且这个属性的值分别是一个对象b和c.会把b和c的属性都合并到一个新的对象里, 作为值返回给合并后的对象.
说的有点复杂, 看个例子.
Java代码

   1.      
   2. jQuery.extend( true,         
   3.          { name: “John”, location: { city: “Boston” } },        
   4.          { last: “Resig”, location: { state: “MA” } }        
   5.      );         
   6.       结果为        
   7.        {  
   8. name: “John”, last: “Resig”,         
   9.         location: { city: “Boston”, state: “MA” }  
  10.        } 


jQuery.extend( true,       
     { name: “John”, location: { city: “Boston” } },      
     { last: “Resig”, location: { state: “MA” } }      
);       
  结果为      
   {
name: “John”, last: “Resig”,       
    location: { city: “Boston”, state: “MA” }
   }


jQuery的做法主要是这样, 先修正参数,根据第一个参数是boolean还是object确定target是哪个对象.
如果有且只有一个object类型的参数, 则把这个参数里的属性copy到jQuery或者jQuery.prototype上, 扩展jQuery对象.用这个办法可以很方便的编写插件.
否则依次遍历第一个object参数之后的所有object类型对象的属性. 根据是否深度继承用不同的方式进行copy操作.
extend会改变第一个参数的引用. 如果不想改变它,可以稍微变通一下,把所有的参数属性都拷贝到一个空对象上.如$.extend({}, obj1, obj2);
Java代码

   1. jQuery.extend = jQuery.fn.extend = function() { 
   2.     var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy, copyIsArray; 
   3.     //target应该指向被拷贝的对象, deep表示深度拷贝或者浅度拷贝, 如果为true,即深度拷贝.反之为浅度拷贝. 
   4.  
   5.     if ( typeof target === "boolean" ) { 
   6.     //如果显示声明了是深度拷贝还是浅度拷贝 
   7.         deep = target; 
   8.     //重新设置deep 
   9.         target = arguments[1] || {}; 
  10.     //修正target, 让它指向第二个参数 
  11.         i = 2; 
  12.     //修正循环开始的位置, copy的时候跳过deep和target, 指向第一个copy对象. 
  13.     } 
  14.  
  15.     if ( typeof target !== "object" && !jQuery.isFunction(target) ) { 
  16.         //修正target. 确保target是一个可以迭代属性的对象, 从这句代码看出target可以为函数对象 
  17.         target = {}; 
  18.     } 
  19.     if ( length === i ) { 
  20.         target = this; 
  21.         --i; 
  22.         /*
  23.             当只有一个参数是对象的情况(可能前面还有一个true或false). 
  24.             把属性赋值给jquery或者jquery.fn. 帮jquery实现继承.
  25.             并且重新修正循环起始位置.
  26.         */ 
  27.     } 
  28.  
  29.     for ( ; i < length; i++ ) { 
  30.         if ( (options = arguments[ i ]) != null ) { 
  31.             //只遍历不为null的参数 
  32.             for ( name in options ) {  //遍历copy对象的属性 
  33.                 src = target[ name ];    //记录target的属性值 
  34.                 copy = options[ name ];  //记录将要被copy进去的值 
  35.                 if ( target === copy ) { //防止死循环??? 
  36.                     continue; 
  37.                 } 
  38.  
  39.                 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { 
  40.                     //如果是深度继承并且copy是对象字面量或者数组, 但必须保证至少为对象字面量和数组之一才会继续下面. 
  41.                     //copyIsArray = jQuery.isArray(copy)返回的是boolean. 
  42.                     //如果jQuery.isPlainObject(copy)和jQuery.isArray(copy)都是false,那整个if语句里的判断条件都返回false, 
  43.                     //也就不会执行下面了的深度继承了.比起1.42版本, 很多地方功能没变, 代码还是明显清晰了很多. 
  44.                     if ( copyIsArray ) {   //如果是数组 
  45.                         copyIsArray = false; //重设copyIsArray 
  46.                         clone = src && jQuery.isArray(src) ? src : []; 
  47.                     //防止深度copy的时候, target为undefined. 此时target应该为对象类型.  
  48.                     //如果target本来为undefined, 要设一个不含属性的默认值, 且类型要跟copy对象保持一致. 
  49.                     } else { 
  50.                         clone = src && jQuery.isPlainObject(src) ? src : {}; 
  51.                     //同上 
  52.                     } 
  53.                     target[ name ] = jQuery.extend( deep, clone, copy ); 
  54.                     //深度继承递归调用extend方法进行合并 
  55.                 } else if ( copy !== undefined ) { 
  56.                     target[ name ] = copy; 
  57.                     //浅度继承直接复制属性, 但是不要带入undefined值. 
  58.                 } 
  59.             } 
  60.         } 
  61.     } 
  62.  
  63.     return target; 
  64.     //返回合并后的对象. 第一个obj参数的引用已经被改变了. 
  65. } 
分享到:
评论

相关推荐

    jquery1.43源码分析(核心部分)

    ### jQuery 1.4.3 源码分析(核心部分) #### 一、引言 随着 Web 技术的不断发展,JavaScript 成为了前端开发中不可或缺的一部分。而在众多 JavaScript 库中,jQuery 几乎成为了网页开发的标准工具之一。本文将...

    jquery1.43源码分析(核心部分)[收集].pdf

    《jQuery 1.4.3 源码分析——核心部分》 jQuery 是一个广泛使用的JavaScript库,它极大地简化了JavaScript的DOM操作、事件处理、动画制作和Ajax交互。在jQuery 1.4.3版本中,其核心部分主要包括选择器引擎、DOM操作...

    jquery1.43源码分析之工具方法.doc

    ### jQuery 1.4.3 源码分析之工具方法 #### 一、概述 在探讨`jQuery 1.4.3`源码中工具方法的细节之前,有必要了解这些工具方法在整个库中的作用与定位。根据文档的描述,这部分内容主要关注于一些常用的工具方法,...

    jQuery源码 jQuery源码 jQuery源码

    jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码jQuery源码...

    Jquery1.2.6 源码分析

    Jquery1.2.6源码分析及源码文件 博文链接:https://jljlpch.iteye.com/blog/234218

    [pdf]Jquery1.2.6源码分析

    该去明白其运行机理和核心源码。但是 jQuery 源码不像其它的类库那样,它有 点晦涩,难懂。这就是本源码分析的原因,让所有使用 jQuery 的读者,能快速 上手 jQuery的源码,并在开发中得心应用。

    Jquery源码分析 源码

    本文将从核心概念、设计模式、主要功能模块等方面进行详尽的jQuery源码分析。 1. **核心概念** - **选择器引擎Sizzle**:jQuery的核心是其高效的选择器引擎Sizzle,它基于CSS选择器实现,能够快速准确地定位DOM...

    jquery源码分析

    jquery源码分析,包括入口技术,选择器入口,以及在选择器使用的时候需要注意的优化思路

    jquery1.2.6源码分析

    jquery1.2.6源码分析 jquery1.2.6源码分析

    jquery api, jquery ui api, jquery源码分析

    1. **选择器(Selectors)**:jQuery 的核心功能之一就是强大的CSS选择器,如 `$("#id")` 选择ID为特定值的元素,`$(".class")` 选择具有特定类的元素,`$("tagname")` 选择所有特定类型的标签。 2. **DOM 操作...

    jQuery源码分析系列.pdf

    ### jQuery源码分析系列知识点概览 #### 一、总体架构与核心概念 - **前言开光**:介绍分析jQuery源码的目的和价值,强调通过深入研究源码,可以学习到先进的设计理念和实践技巧。 - **总体架构**:解析jQuery的...

    Jquery1.2.6源码分析

    《jQuery 1.2.6 源码分析》 jQuery是JavaScript库的里程碑之作,它的出现极大地简化了网页的DOM操作,事件处理,动画设计和Ajax交互。本篇文章将聚焦于jQuery 1.2.6版本的源码,深入剖析其内部机制,帮助开发者更好...

    Jquery1.2.6源码分析教程

    1. **选择器引擎(Sizzle)**:jQuery 的核心之一是 Sizzle 选择器引擎,它能够解析 CSS 选择器并高效地查找匹配的 DOM 元素。Sizzle 使用正则表达式和遍历树的算法来提高性能,即使在旧版浏览器中也能保持良好的...

    Jquery1.2.6源码分析+源代码

    **jQuery 1.2.6 源码分析** jQuery 是一个广泛使用的 JavaScript 库,它简化了 HTML 文档遍历、事件处理、动画以及 Ajax 交互等任务。本篇文章将深入探讨 jQuery 1.2.6 版本的源码,揭示其背后的实现原理,帮助...

    jQuery源码分析

    jQuery源码分析 00 前言开光 01 总体架构 03 构造jQuery对象-源码结构和核心函数 03 构造jQuery对象-工具函数 05 异步队列 Deferred 08 队列 Queue 09 属性操作 10 事件处理-Event-概述和基础知识 15 AJAX-前置过滤...

    Jquery1.26版本的源码分析

    Jquery1.26版本的源码分析,pdf格式文档,相当清淅的书籍,前端开发者必看的类库书籍,虽然网上有很多jQuery的实例,但真正深入分析介绍jQuery源码细节的文档太少了,所以本篇文稿希望是你期待的,文档内容包括:...

    jQuery源码分析(1.7)

    ### jQuery源码分析关键知识点详解 #### 一、前言 在深入了解jQuery源码之前,有必要先简要介绍一下jQuery的基本情况及其对JavaScript编程领域的重要意义。jQuery作为一个轻量级、功能丰富的JavaScript库,在Web...

    jQuery 2.0.3 源码分析之core(一)整体架构

    在深入探讨jQuery 2.0.3源码中core部分的整体架构之前,我们先简要回顾一下jQuery框架的核心功能和其设计思想。jQuery是一个广泛使用的JavaScript库,主要用于简化HTML文档遍历和操作、事件处理、动画和Ajax交互。它...

    JQuery源码详细中文注释_Jquery源码分析_

    总结,jQuery源码分析涉及的内容广泛,包括选择器引擎、事件处理、DOM操作、动画效果、Ajax通信以及插件开发等多个方面。深入理解jQuery源码,有助于我们更好地利用它来构建高效、易维护的Web应用。通过阅读中文注释...

Global site tag (gtag.js) - Google Analytics