- 浏览: 520458 次
- 性别:
- 来自: 北京
博客专栏
-
jQuery技术内幕
浏览量:200456
最新评论
-
青春依旧:
学习html5哪里好?当然华清远见是首选!
[原创] jQuery源码分析-01总体架构 -
追梦1819:
[size=x-small][color=red][/colo ...
[原创] jQuery源码分析-04 选择器-Sizzle-设计思路 -
niuqiang2008:
学习学习
[原创] jQuery源码分析-04 选择器-Sizzle-工作原理 -
liuweihug:
jquery 解析正则表达式及常见的Regex规则和表达式 - ...
[原创] jQuery源码分析-02正则表达式-RegExp-常用正则表达式 -
liang8768:
mark!!!
[原创] jQuery源码分析-00前言开光
作者:nuysoft/高云 QQ:47214707 EMail:nuysoft@gmail.com
声明:本文为原创文章,如需转载,请注明来源并保留原文链接。
读读写写,不对的地方请告诉我,多多交流共同进步,本章的的PDF下载在最后。
前记:
国庆给自己放了个安静的长假,日游杭州大小景点,夜宿西湖边上,于大街小巷中遍尝美味小吃,没有电脑没有网络,这样的日子真是是好日子啊;回京开始工作了,编程是我的兴趣,虽然变成了工作,但是享受的心态要继续保持下去。
白天工作,不管忙不忙,jQuery源码分析系列只能放在晚上写,经常看的朋友兴许也注意到更新时间一般是凌晨,经常觉的挺累的,想今天算了吧上床睡觉明天再说吧,但还是坚持下来了,我会尽量要求自己1-2天发布一篇。刚开始写的时候,有些担心写出来的东西会幼稚肤浅或讲不清楚,有些地方也确实是这样,这个假期让我想明白了很多事,以后的文章欢迎各位道友拍各种砖和石头,用力点,不要停。
8. 队列 Queue
8.1 概述
队列是一种特殊的线性表,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队)。队列的特点是先进先出(FIFO-first in first out),即最先插入的元素最先被删除。
jQuery提供了jQuery.queue/dequeue和jQuery.fn.queue/dequeue,实现对队列的入队、出队操作。不同于队列定义的是,jQuery.queue和jQuery.fn.queue不仅执行出队操作,返回队头元素,还会自动执行返回的队头元素。
8.2 用途
在jQuery源码中,仅用于动画模块,这里入队的函数的功能是:
l 遍历要动画的属性,修正要执行的动画动作,修正/备份属性
l 遍历要动画的属性,为每一个属性创建jQuery.fx对象,计算起始值和结束值,调用fx对象的custom开始动画
看看源码:
/** * .animate( properties, [duration], [easing], [complete] ) * * .animate( properties, options ) * * animate做了三件事: * 1. 调用jQuery.speed修正传入的参数(时间、算法、回调函数) * 2. 遍历要动画的属性,修正要执行的动画动作,修正/备份属性 * 3. 遍历要动画的属性,为每一个属性创建jQuery.fx对象,计算起始值和结束值,调用fx对象的custom开始动画 */ animate: function( prop, speed, easing, callback ) { var optall = jQuery.speed(speed, easing, callback); // 修正参数
// 如果是空对象,则直接运行callback if ( jQuery.isEmptyObject( prop ) ) { return this.each( optall.complete, [ false ] ); }
// Do not change referenced properties as per-property easing will be lost // 复制一份prop,不改变原有的属性 prop = jQuery.extend( {}, prop ); // queue为false,动画立即开始,否则则放入动画队列 return this[ optall.queue === false ? "each" : "queue" ](function() { // XXX 'this' does not always have a nodeName when running the // test suite
if ( optall.queue === false ) { jQuery._mark( this ); }
var opt = jQuery.extend( {}, optall ), // 复制一份 isElement = this.nodeType === 1, hidden = isElement && jQuery(this).is(":hidden"), // 是否隐藏 name, val, p, display, e, parts, start, end, unit;
// will store per property easing and be used to determine when an animation is complete // 已完成的属性 opt.animatedProperties = {};
// 遍历每一个属性,本次遍历仅仅是修正和记录属性值,为后边的动画做准备 for ( p in prop ) { // property name normalization // property格式化,转换为驼峰式,因为style的属性名是驼峰式 name = jQuery.camelCase( p ); if ( p !== name ) { prop[ name ] = prop[ p ]; delete prop[ p ]; }
val = prop[ name ];
// easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default) // 样式值允许以数组的方式,数组第一个表示动画是否完成,第二个表示动画的值,这样做有两点好处: // 1. 允许在某些属性还未完成时就执行回调函数、执行下一个动画 // 2. 允许为默写属性指定算法 if ( jQuery.isArray( val ) ) { opt.animatedProperties[ name ] = val[ 1 ]; // val = prop[ name ] = val[ 0 ]; } else { // 对指定属性设置动画算法 opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing'; }
// 如果属性值是hide,并且已经隐藏,则直接调用回调函数;show同理 if ( val === "hide" && hidden || val === "show" && !hidden ) { return opt.complete.call( this ); }
// 如果是width/height,修正它的: // overflow // display if ( isElement && ( name === "height" || name === "width" ) ) { // Make sure that nothing sneaks out // Record all 3 overflow attributes because IE does not // change the overflow attribute when overflowX and // overflowY are set to the same value opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
// Set display property to inline-block for height/width // animations on inline elements that are having width/height // animated if ( jQuery.css( this, "display" ) === "inline" && jQuery.css( this, "float" ) === "none" ) { if ( !jQuery.support.inlineBlockNeedsLayout ) { this.style.display = "inline-block";
} else { display = defaultDisplay( this.nodeName );
// inline-level elements accept inline-block; // block-level elements need to be inline with layout if ( display === "inline" ) { this.style.display = "inline-block";
} else { this.style.display = "inline"; this.style.zoom = 1; } } } } }
// 如果是width/height,先设置为超出部分隐藏 if ( opt.overflow != null ) { this.style.overflow = "hidden"; }
// 遍历每一个属性, for ( p in prop ) { e = new jQuery.fx( this, opt, p ); // 构造动画fx对象 val = prop[ p ];
// 如果是toggle/show/hide, if ( rfxtypes.test(val) ) { // 如果是toggle,则判断当前是否hidden,如果hidden则show,否则hide // 如果不是toggle,说明val是hide/show之一 e[ val === "toggle" ? hidden ? "show" : "hide" : val ]();
} else { parts = rfxnum.exec( val ); // 数值型,1+=/-=,2数值,3单位 start = e.cur(); // 取出当前值
// 如果是数值型 if ( parts ) { end = parseFloat( parts[2] ); // 包括了正负号 unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" ); // jQuery.cssNumber中包含的是无单位的数值型属性,比如zIndex/zoom
// We need to compute starting value // 计算开始值 if ( unit !== "px" ) { jQuery.style( this, p, (end || 1) + unit); start = ((end || 1) / e.cur()) * start; jQuery.style( this, p, start + unit); }
// If a +=/-= token was provided, we're doing a relative animation // 如果以+=/-=开头,则做相对动画 if ( parts[1] ) { // 计算出相对值 end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start; } // 开始执行动画 e.custom( start, end, unit ); // 不是数值型 } else { // 开始执行动画 e.custom( start, val, "" ); } } }
// For JS strict compliance // 严格遵守? return true; }); } |
8.3 实现思路
jQuery队列的实现依赖于jQuery.data,用数组实现,作为私有数据存储在jQuery的全局变量jQuery.cache中。
l 调用jQuery.queue入队时,如果不传入队列名,则默认为fx(标准动画)
n 队列用数组实现,入队直接调用数组对象的方法push
n 入队的元素必须是函数,或由函数构成的数组
n 所有队列名会自动加上queue后缀,表示这是一个队列
n 如果传入的是数组,则覆盖现有的队列
n 如果不是数组,则直接入队
l 调用jQuery. dequeue出队时,会先调用jQuery.queue取得整个队列,因为队列用数组实现,可以调用数组的shift方法取出第一个元素并执行
n 执行第一个元素时采用function.call( context, args ),由此可以看出jQuery队列只支持函数(这么说不完全准确,fx动画是个特例,会在队列头端插入哨兵inprogress,类型为字符串)
n 出队的元素会自动执行,无论这个元素是不是函数,如果不是函数此时就会抛出异常(这个异常并没有处理)
n 如果队列变成空队列,则用关键delete删除jQuery.cache中type对应的属性
8.4 验证(firefox+firebug)
我们验证一下上面的思路:
1. 先入队3个弹窗函数,分别弹出1、2、3
$('body').queue( 'test', function(){ alert(1); } ) $('body').queue( 'test', function(){ alert(2); } ) $('body').queue( 'test', function(){ alert(3); } ) |
2. 查看jQuery.data为body分配的唯一id(为什么要查看body的唯一id,请参考数据缓存的解析)
>>> $.expando "jQuery161017518149125935123" command: >>> $('body')[0][$.expando] 5 >>> $('body')[0]["jQuery161017518149125935123"] 5 |
$.expando有三部分构成:字符串"jQuery" + 版本号jQuery.fn.jquery + 随机数Math.random(),因此每次加载页面后都不相同。
3. 查看jQuery.cache对属性5对应的数据,格式化如下:
{ "1" : { ... }, "2" : { ... }, "3" : { ... }, "4" : { ... }, "5" : { "jQuery161017518149125935123" : { "testqueue" : [ (function () {alert(1);}), (function () {alert(2);}), (function () {alert(3);}) ] } } } |
内部数据存储在$.expando属性("jQuery161017518149125935123")中,这点区别于普通数据
4. 外事具备,我们出队试试,连续3次调用出队$('body').dequeue( 'test' ),每次调用dequeue后用$('body').queue('test').length检查队列长度
控制台命令 |
$('body').dequeue( 'test' ) |
$('body').dequeue( 'test' ) |
$('body').dequeue( 'test' ) |
浏览器截图 |
见附件PDF | 见附件PDF | 见附件PDF |
队列长度 |
2 |
1 |
0 |
果不其然,调用出队函数dequeue后,入队的函数按照先进先出的顺序,依次被执行
5. 最后看看全部出队后,jQuery.cache中的状态
>>> $.cache[5][$.expando]['testqueue'] undefined |
可以看到,testqueue属性已经从body的缓存中移除
8.5 源码分析
jQuery.extend({ // 计数器,用在动画animate中 _mark: function( elem, type ) { if ( elem ) { type = (type || "fx") + "mark"; // 取出数据加1,存储在内部对象上 jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true ); } }, // 用在动画animate中 _unmark: function( force, elem, type ) { if ( force !== true ) { type = elem; elem = force; force = false; } if ( elem ) { type = type || "fx"; var key = type + "mark", // 减1 count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 ); if ( count ) { jQuery.data( elem, key, count, true ); } else { jQuery.removeData( elem, key, true ); handleQueueMarkDefer( elem, type, "mark" ); } } }, // jQuery.queue( element, [queueName] ) // 返回在指定的元素element上将要执行的函数队列
// jQuery.queue( element, queueName, newQueue or callback ) // 修改在指定的元素element上将要执行的函数队列 // 使用jQuery.queue添加函数后,最后要调用jQuery.dequeue(),使得下一个函数能线性执行 // // 调用jQuery.data,存储为内部数据(pvt为true) queue: function( elem, type, data ) { // elem必须存在,否则没有意义 if ( elem ) { type = (type || "fx") + "queue"; // 改名,每个都要加上queue // 取出队列 var q = jQuery.data( elem, type, undefined, true ); // Speed up dequeue by getting out quickly if this is just a lookup // 如果data存在,才会进行后边转换数组、入队等操作,可以加速取出整个队列 if ( data ) { // 如果队列不存在,或者data是数组,则调用makeArray转换为数组,并覆盖队列(入队) if ( !q || jQuery.isArray(data) ) { // 用数组实现队列 q = jQuery.data( elem, type, jQuery.makeArray(data), true ); } else { // 队列存在的话,且data不是数组,直接入队 // 这里并没有判断data的类型,不管data是不是函数 q.push( data ); } } // 返回队列(即入队的同时,返回整个队列) // 简洁实用的避免空引用的技巧 return q || []; } }, // 出队并执行 // 调用jQuery.queue取得整个队列,在调用shift取出第一个元素 dequeue: function( elem, type ) { type = type || "fx"; // 默认fx,但是入队时不是被改为fxqueue了么,别着急!
var queue = jQuery.queue( elem, type ), // 取出队列,调用queue时type变成了type+quque fn = queue.shift(), // 取出第一个 defer;
// If the fx queue is dequeued, always remove the progress sentinel // 如果取出的fn是一个正在执行中标准动画fx,抛弃执行哨兵(inprogress),再取一个 if ( fn === "inprogress" ) { fn = queue.shift(); }
if ( fn ) { // Add a progress sentinel to prevent the fx queue from being // automatically dequeued // 如果是标准动画,则在队列头部增加处理中哨兵属性,阻止fx自动处理 if ( type === "fx" ) { // 在队列头部增加哨兵inprogress queue.unshift("inprogress"); } // 执行取出的fn,并传入回调函数jQuery.dequeue // 可以看到fn必须是函数,否则会出错 fn.call(elem, function() { // 但是这个回调函数不会自动执行 jQuery.dequeue(elem, type); }); } // 如果执行完毕,则调用jQuery.removeData移除type指定的队列 // 此时的队列成为空队列,实质是一个空数组,jQuery.removeData内部使用delete关键字删除type对应的空数组 if ( !queue.length ) { jQuery.removeData( elem, type + "queue", true ); handleQueueMarkDefer( elem, type, "queue" ); } } });
jQuery.fn.extend({ // queue( [ queueName ] ) // 返回在指定的元素element上将要执行的函数队列 // // queue( [ queueName ], newQueue or callback ) // 修改在指定的元素element上将要执行的函数队列 queue: function( type, data ) { // 修正参数:只传了一个非字符串的参数,则默认为动画fx if ( typeof type !== "string" ) { data = type; type = "fx"; } // 如果data等于undefined,则认为是取队列 if ( data === undefined ) { return jQuery.queue( this[0], type ); } // 如果传入了data参数,则在每一个匹配的元素上执行入队操作 // 在jQuery中,使用each遍历匹配的元素,是一种安全的惯例做法 return this.each(function() { var queue = jQuery.queue( this, type, data ); // 如果动画执行完毕(即不是inprogress),则从队列头部移除 if ( type === "fx" && queue[0] !== "inprogress" ) { jQuery.dequeue( this, type ); } }); }, // 调用jQuery.dequeue出队 dequeue: function( type ) { return this.each(function() { jQuery.dequeue( this, type ); }); }, // Based off of the plugin by Clint Helfers, with permission. // http://blindsignals.com/index.php/2009/07/jquery-delay/ // 延迟执行队列中未执行的函数,通过在队列中插入一个延时 出队的函数来实现 delay: function( time, type ) { time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; type = type || "fx";
return this.queue( type, function() { var elem = this; setTimeout(function() { jQuery.dequeue( elem, type ); }, time ); }); }, // 清空队列,通过将第二参数设置为空数组[] clearQueue: function( type ) { return this.queue( type || "fx", [] ); }, // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) // 返回一个只读视图,当队列中指定类型的函数执行完毕后 promise: function( type, object ) { if ( typeof type !== "string" ) { object = type; type = undefined; } type = type || "fx"; var defer = jQuery.Deferred(), elements = this, i = elements.length, count = 1, deferDataKey = type + "defer", queueDataKey = type + "queue", markDataKey = type + "mark", tmp; function resolve() { if ( !( --count ) ) { defer.resolveWith( elements, [ elements ] ); } } while( i-- ) { if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) { count++; tmp.done( resolve ); } } resolve(); return defer.promise(); } }); |
8.6 总结
jQuery队列的实现并不复杂,它的核心思路是:
在传统队列的实现上增加了出队自动执行,执行完成后再次自动出队。
详见小节:8.3 实现思路
- _原创__jQuery源码分析-08队列-Queue.pdf (396.5 KB)
- 下载次数: 332
评论
其实我只希望能做一个任务队列,和DOM完全没毛关系~~~
1. jQuery 的 Queue 模块依赖于 Data 模块实现。
2. 简单的任务队列可以通过数组实现,push() 入队,shift() 出队。
其实我只希望能做一个任务队列,和DOM完全没毛关系~~~
发表评论
-
[原创] jQuery源码分析-04 选择器-Sizzle-设计思路
2011-11-14 20:59 6835作者:nuysoft/高云 QQ:47214707 Em ... -
[原创] jQuery源码分析-04 选择器-Sizzle-工作原理
2011-11-13 23:45 7870作者:nuysoft/高云 QQ:47214707 EM ... -
[原创] jQuery源码分析-02正则表达式-RegExp-常用正则表达式
2011-10-27 01:29 46604作者:nuysoft/JS攻城师/ ... -
[原创] jQuery源码分析-jQuery中的循环技巧
2011-10-27 00:36 14534作者:nuysoft/JS攻城师/高云 QQ:47214707 ... -
[原创] jQuery源码分析-10事件处理-Event-DOM-ready
2011-10-20 01:20 10581作者:nuysoft/JS攻城师/ ... -
[原创] jQuery源码分析-Java工程师应该向jQuery学习的8点建议
2011-10-18 23:56 12227作者:nuysoft/高云 QQ:47214707 EM ... -
[原创] jQuery源码分析-10事件处理-Event-事件绑定与删除-bind/unbind+live/die+delegat/undelegate
2011-10-18 22:31 15647作者:nuysoft/高云 QQ:47214707 EM ... -
[原创] jQuery源码分析-10事件处理-Event-源码结构
2011-10-17 01:01 12100作者:nuysoft/高云 QQ:47214707 EMail ... -
[原创] jQuery源码分析-07数据缓存-Cache
2011-10-13 19:55 12104作者:nuysoft/高云 QQ:47214707 EMail ... -
[原创] jQuery源码分析-06浏览器测试-Support
2011-10-13 19:19 9878作者:nuysoft/高云 QQ:47214707 EMail ... -
[原创] jQuery源码分析系列目录(持续更新)
2011-10-12 12:30 29317作者:nuysoft/高云 QQ:47214707 E ... -
[原创] jQuery源码分析-10事件处理-Event-概述和基础知识
2011-10-12 00:16 11988作者:nuysoft/高云 Q ... -
[原创] jQuery源码分析-03构造jQuery对象-工具函数
2011-09-29 23:21 29760作者:nuysoft/高云 QQ:47214707 E ... -
[原创] jQuery源码分析-15AJAX-类型转换器
2011-09-29 02:25 7390作者:nuysoft/高云 QQ:47214707 EM ... -
[原创] jQuery源码分析-03构造jQuery对象-源码结构和核心函数
2011-09-28 02:20 52929作者:nuysoft/高云 QQ:47214707 EMail ... -
[原创] jQuery源码分析-如何做jQuery源码分析
2011-09-27 00:22 14982近期在ITEYE陆续写了几篇jQuery源码分析,乐在其 ... -
[原创] jQuery源码分析-17尺寸和大小 Dimensions & Offset
2011-09-25 22:05 7032边读边写,不正确的地方,还请各位告诉我,多多交流共同学习, ... -
[原创] jQuery源码分析-15AJAX-前置过滤器和请求分发器
2011-09-23 00:09 13262边读边写,不正确的 ... -
[原创] jQuery源码分析-00前言开光
2011-09-21 23:42 46980jQuery源码分析 - 前言 jQuery凭借简洁的语法和 ... -
[原创] jQuery源码分析-01总体架构
2011-09-21 23:31 751041. 总体架构 1.1 自调用匿名函 ...
相关推荐
### jQuery源码分析系列知识点概览 #### 一、总体架构与核心概念 - **前言开光**:介绍分析jQuery源码的目的和价值,强调通过深入研究源码,可以学习到先进的设计理念和实践技巧。 - **总体架构**:解析jQuery的...
jQuery源码分析 00 前言开光 01 总体架构 03 构造jQuery对象-源码结构和核心函数 03 构造jQuery对象-工具函数 05 异步队列 Deferred 08 队列 Queue 09 属性操作 10 事件处理-Event-概述和基础知识 15 AJAX-前置过滤...
### jQuery源码分析关键知识点详解 #### 一、前言 在深入了解jQuery源码之前,有必要先简要介绍一下jQuery的基本情况及其对JavaScript编程领域的重要意义。jQuery作为一个轻量级、功能丰富的JavaScript库,在Web...
深入理解jQuery源码有助于开发者更好地利用其API并优化性能。主要关注点包括: 1. **选择器引擎(Sizzle)**:jQuery 使用Sizzle作为其选择器引擎,它是如何快速高效地解析CSS选择器并找到匹配元素的。 2. **链式...
源码分析是理解jQuery内部实现和深入学习JavaScript编程的一个重要途径。本文将从jQuery的总体架构入手,介绍其源码的编写风格和结构设计。 首先,当我们打开jQuery的源码,会发现其代码结构是以一个自调用匿名函数...
jQuery源码分析系列涉及了对jQuery库内部实现的详细解读,jQuery作为前端开发中最常用的JavaScript库之一,它简化了DOM操作、事件处理、动画效果和AJAX交互等操作。通过深入分析jQuery的源码,开发者可以学习到先进...
在深入讨论jQuery源码中Callbacks模块的内部实现之前,先要明确什么是Callbacks以及它在JavaScript编程中的作用。Callbacks,即回调函数,在JavaScript中扮演着至关重要的角色。由于JavaScript是基于单线程事件循环...
本文将详细探讨jQuery队列的源码结构、基本使用方法以及源码分析。 1. 源码结构 jQuery的队列功能由`.extend()`和`.fn.extend()`方法扩展到jQuery核心和jQuery对象上。主要的方法包括: - `jQuery.extend({queue:...
这份中文注释的源码分析旨在帮助开发者逐步揭开jQuery的神秘面纱,提升JavaScript编程技能。 首先,jQuery的核心在于选择器引擎Sizzle。Sizzle是一个高效的选择器解析器,能够处理CSS1至CSS3的选择器,包括ID、类、...
《jQuery源码分析》 jQuery,作为一款广泛使用的JavaScript库,极大地简化了DOM操作、事件处理、动画制作以及Ajax交互。其源码的深度分析对于理解JavaScript编程原理、提升前端开发技能具有重大意义。本篇将针对...
《jQuery 源码分析——揭示JavaScript库的精髓》 jQuery,这个JavaScript库自2006年诞生以来,就以其简洁的API和强大的功能深受开发者喜爱,它极大地简化了DOM操作、事件处理、动画效果以及Ajax交互。本文将深入...
jQuery中的queue函数是一个非常强大的工具,它用于维护一系列函数的调用顺序,这些函数被存储在一个队列中并异步执行,不会阻塞主程序的运行。queue函数允许开发者将回调函数以队列的方式加入,以达到控制函数执行...
《jQuery源码解析:深入理解JavaScript库之精华》 jQuery,作为一款广泛应用于Web开发的JavaScript库,以其简洁的API和强大的功能深受开发者喜爱。"Write Less, Do More"是其核心理念,它极大地简化了DOM操作、事件...
在详细分析jQuery 1.9.1源码系列的文章中,我们集中于探讨了jQuery库中常用工具函数的实现原理及其应用。这些工具函数广泛用于动画处理、事件处理、数据操作和性能优化等场景。本篇文章详细解释了多个核心工具函数的...
### jQuery源码逐步解析知识点概览 #### 一、jQuery简介 jQuery是一个快速、简洁的JavaScript库,使得用户能更方便地处理HTML文档、事件、实现动画效果,并且方便地为网站提供AJAX交互。其核心特性可以总结为:简化...
jQuery 1.9.1版本的源码分析系列文章,专注于揭示jQuery库内部运作的机制。在本系列的第十五篇文章中,特别关注了动画处理机制,而这一部分是jQuery对开发者而言非常实用的功能,允许开发者以简便的方式给网页元素...
《锋利的jQuery源码》是一本深入剖析jQuery核心机制的书籍,对于任何希望深入了解这一广泛使用的JavaScript库的开发者来说,都是不可或缺的资源。jQuery以其简洁的语法、强大的功能和良好的浏览器兼容性,成为了Web...