解决方案 »
- 大家帮忙给cookie增加个删除
- 请教有关表单提交时要触发显示一个动态图片的ie下实现
- 麻烦您看看这个段对联广告代码哪里有问题,谢谢!
- 发现一个很好的样式,谁能帮我看看是怎么做的?谢谢了
- 如何JS就是获取验证码图片的消息头???
- 请INPUT的属性为READONLY,我想在添入一个数值进去INPUT的时候触发事件,请问是哪个事件呢?
- 请问如何在网页中实现onmousedown和ondblclick
- 请教:JS如何控制EMBED的SRC阿 ?
- 下拉框怎么控制的?
- 刚看的js,自己敲得时候就是不知道怎么回事 求解答啊。。
- jquery.uploadify上传图片问题
- 有没有带“文章版本”功能的富文本编辑器?或如何拓展现有功能实现?
HANDLE是句柄
element 肯定是元素,楼主一会儿元素一会儿事件的。事件和元素是两码东西
文字完全按英文顺序直译
呃直接用google translate好了。
// The number of elements contained in the matched element set
// 匹配事件集包含事件的数量这句翻译的是在不专业
//匹配html或者确认对于#id没有环境是规定的
trigger: function( event, data, elem, onlyHandlers ) {
var handle, ontype, cur,
bubbleType, special, tmp, i,
eventPath = [ elem || document ],
type = core_hasOwn.call( event, "type" ) ? event.type : event,
namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; cur = tmp = elem = elem || document; // Don't do events on text and comment nodes
// 不要在文本和内容节点上做事件
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
return;
} // focus/blur morphs to focus in/out; ensure we're not firing them right now
// 聚焦/模糊 改变为了聚焦 内/外;确保我们现在没有解雇他们(这里fire中文怎么翻译暂时没想好,先翻译为解雇)
if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
return;
} if ( type.indexOf(".") >= 0 ) {
// Namespaced trigger; create a regexp to match event type in handle()
// 命名空间触发;用handle()函数创建一个正则表达式匹配的事件类型
namespaces = type.split(".");
type = namespaces.shift();
namespaces.sort();
}
ontype = type.indexOf(":") < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string
// 访问者可以通过一个jQuery事件对象,对象,或者只是一个事件类型字符串
event = event[ jQuery.expando ] ?
event :
new jQuery.Event( type, typeof event === "object" && event ); event.isTrigger = true;
event.namespace = namespaces.join(".");
// 用符号(.)将命名空间连接起来
event.namespace_re = event.namespace ?
new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
null; // Clean up the event in case it is being reused
// 清理事件以免它被重用
event.result = undefined;
if ( !event.target ) {
event.target = elem;
} // Clone any incoming data and prepend the event, creating the handler arg list
// 克隆任何输入的数据并且前面加上事件,创建处理程序的参数列表
data = data == null ?
[ event ] :
jQuery.makeArray( data, [ event ] ); // Allow special events to draw outside the lines
// 允许特殊事件为了利用外部的线(draw outside the lines暂时按字面翻译)
special = jQuery.event.special[ type ] || {};
if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
return;
} // Determine event propagation path in advance, per W3C events spec (#9951)
// 提前确定事件的传输路径,根据W3C事件标准(#9951)
// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
// Bubble up(冒泡)对文档,然后对window; 观察一个全局ownerDocument变量(#9724)
if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { bubbleType = special.delegateType || type;
if ( !rfocusMorph.test( bubbleType + type ) ) {
cur = cur.parentNode;
}
for ( ; cur; cur = cur.parentNode ) {
eventPath.push( cur );
tmp = cur;
} // Only add window if we got to document (e.g., not plain obj or detached DOM)
if ( tmp === (elem.ownerDocument || document) ) {
eventPath.push( tmp.defaultView || tmp.parentWindow || window );
}
} // Fire handlers on the event path
// 在事件的处理途径上解雇处理者(Fire handlers暂时译为:解雇处理者,没想到更好的翻译)
i = 0;
while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { event.type = i > 1 ?
bubbleType :
special.bindType || type; // jQuery handler
// jQuery处理
handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
if ( handle ) {
handle.apply( cur, data );
} // Native handler
// Native处理
handle = ontype && cur[ ontype ];
// Create scrollLeft and scrollTop methods
// 创建滚动左面和滚动顶部方法
jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
var top = /Y/.test( prop ); jQuery.fn[ method ] = function( val ) {
return jQuery.access( this, function( elem, method, val ) {
var win = getWindow( elem ); if ( val === undefined ) {
return win ? (prop in win) ? win[ prop ] :
win.document.documentElement[ method ] :
elem[ method ];
} if ( win ) {
win.scrollTo(
!top ? val : jQuery( win ).scrollLeft(),
top ? val : jQuery( win ).scrollTop()
); } else {
elem[ method ] = val;
}
}, method, val, arguments.length, null );
};
});function getWindow( elem ) {
return jQuery.isWindow( elem ) ?
elem :
elem.nodeType === 9 ?
elem.defaultView || elem.parentWindow :
false;
}
// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
// 创建内层高度、内层宽度、高度、宽度、整个窗口高度(outerHeight)和整个窗口的宽度(outerWidth)方法
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
// margin is only for outerHeight, outerWidth
// 框架只是为了整个窗口高度和整个窗口的宽度
jQuery.fn[ funcName ] = function( margin, value ) {
var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); return jQuery.access( this, function( elem, type, value ) {
var doc; if ( jQuery.isWindow( elem ) ) {
// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
// 截至2012年5月8日对于移动Safari(苹果公司浏览器)产生不正确的结果,但是
// isn't a whole lot we can do. See pull request at this URL for discussion:
// 没有太多我们可做的。请求在这个URL地址进行讨论(See pull 感觉这里做动词不用翻译)
// https://github.com/jquery/jquery/pull/764
// 以上是网址
return elem.document.documentElement[ "client" + name ];
} // Get document width or height
// 得到文档的宽度或者高度
if ( elem.nodeType === 9 ) {
doc = elem.documentElement; // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
// 滚动(宽度或者高度)或者返回(宽度或者高度)或者客户端(宽度或者高度),无论哪个都是最大的
// unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
// 不幸的是,这将导致错误#3838仅在IE6和IE8,但目前还没有很好的、精巧的方法解决它。
return Math.max(
elem.body[ "scroll" + name ], doc[ "scroll" + name ],
elem.body[ "offset" + name ], doc[ "offset" + name ],
doc[ "client" + name ]
);
} return value === undefined ?
// Get width or height on the element, requesting but not forcing parseFloat
// 得到宽度或高度的元素,要求但不强迫转换为浮点数
jQuery.css( elem, type, extra ) : // Set width or height on the element
// 设置宽度或高度的元素
jQuery.style( elem, type, value, extra );
}, type, chainable ? margin : undefined, chainable, null );
};
});
});
// Limit scope pollution from any deprecated API
// 从任何过时的API的限制范围污染(scope pollution暂时译为范围污染)
// (function(){})();
// 上句解释应该是对function(){}外又加了()进行解释,范围污染
// Expose jQuery to the global object
// 揭露jQuery作为全局对象
window.jQuery = window.$ = jQuery;// Expose jQuery as an AMD module, but only for AMD loaders that
// 揭露jQuery为AMD的模块,但只有AMD的装载机
// understand the issues with loading multiple versions of jQuery
// 了解问题与加载多个版本的jQuery
// in a page that all might call define(). The loader will indicate
// 在一个网页有所有可能访问define()函数。加载程序将显示
// they have special allowances for multiple jQuery versions by
// 他们有特别的补助为多个版本的jQuery以(接下面)
// specifying define.amd.jQuery = true. Register as a named module,
// 指定定义。amd。jQuery为真。注册作为一个被命名的模板,
// since jQuery can be concatenated with other files that may use define,
// 由于jQuery可以被其他能使用的定义文件链接
// but not use a proper concatenation script that understands anonymous
// 而不是使用适当的链接脚本了解匿名
// AMD modules. A named AMD is safest and most robust way to register.
// ADM模板。一个命名AMD是最安全和最可靠的方法对于注册
// Lowercase jquery is used because AMD module names are derived from
// 小写jquery被使用因为AMD模块的名字是源于
// file names, and jQuery is normally delivered in a lowercase file name.
// 文件名字,并且jQuery通常源于小写文件名。
// Do this after creating the global so that if an AMD module wants to call
// 在创建全局后这么做以便一个AMD模板想要访问
// noConflict to hide this version of jQuery, it will work.
// 无冲突为了隐藏此版本的jQuery,它将会工作。
if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
define( "jquery", [], function () { return jQuery; } );
}})( window );
/*
* 1.传入window
* 通过传入window变量,使得window由全局变量变为局部变量
* 当在jQuery代码块中访问window时,不需要将作用域链回退到顶层作用域,这样可以更快的访问window
* 更重要的是,将window作为参数传入,可以在压缩代码时进行优化
* (function(a,b){})(window);window 被优化为 a,压缩后文件更小
* 2.传入undefined
* 是为了在“自调用匿名函数”的作用域内,确保undefined是真的未定义
* 因为undefined能够被重写,赋予新的值
* undefined = "now it's defined";
* alert( undefined );
* 浏览器测试结果:
* 浏览器测试结果结论
* ienow it's defined可以改变
* firefoxundefined不能改变
* chromenow it's defined可以改变
* operanow it's defined可以改变
*/
看源码说明你是个爱钻研的人,但是这样玩的话就跟汉化api文档一样,搞不好会给自己带来了思维定势,比如你翻译的某一个地方有点误区,以后你任何时候都会这样固化你的思维,长此以往,你可能就带来巨大的错误在里面
jQuery.offset = { setOffset: function( elem, options, i ) {
var position = jQuery.css( elem, "position" ); // set position first, in-case top/left are set even on static elem
// 首先设置位置。in-case 顶部/左边被设置在静止的elem上
if ( position === "static" ) {
elem.style.position = "relative";
} var curElem = jQuery( elem ),
curOffset = curElem.offset(),
curCSSTop = jQuery.css( elem, "top" ),
curCSSLeft = jQuery.css( elem, "left" ),
calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
props = {}, curPosition = {}, curTop, curLeft; // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
// 需要能够计算出位置如果顶部或者左边是自动设置并且位置是绝对的或者固定(参考CSS样式设计)
if ( calculatePosition ) {
curPosition = curElem.position();
curTop = curPosition.top;
curLeft = curPosition.left;
} else {
curTop = parseFloat( curCSSTop ) || 0;
curLeft = parseFloat( curCSSLeft ) || 0;
} if ( jQuery.isFunction( options ) ) {
options = options.call( elem, i, curOffset );
} if ( options.top != null ) {
props.top = ( options.top - curOffset.top ) + curTop;
}
if ( options.left != null ) {
props.left = ( options.left - curOffset.left ) + curLeft;
} if ( "using" in options ) {
options.using.call( elem, props );
} else {
curElem.css( props );
}
}
};
jQuery.fn.extend({ position: function() {
if ( !this[ 0 ] ) {
return;
} var offsetParent, offset,
parentOffset = { top: 0, left: 0 },
elem = this[ 0 ]; // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
// 固定元素从窗口偏移(父元素偏移 = {top:0, left: 0},因为它只是偏移的父元素
if ( jQuery.css( elem, "position" ) === "fixed" ) {
// we assume that getBoundingClientRect is available when computed position is fixed
// 我们假设 getBoundingClientRect 是有效的,当计算的位置是固定的
offset = elem.getBoundingClientRect();
} else {
// Get *real* offsetParent
// 得到真正的返回父元素
offsetParent = this.offsetParent(); // Get correct offsets
// 得到错误的返回设置
offset = this.offset();
if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
parentOffset = offsetParent.offset();
} // Add offsetParent borders
// 增加返回父元素的边界
parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
} // Subtract parent offsets and element margins
// 父元素的偏移和元素的边缘相减
// note: when an element has margin: auto the offsetLeft and marginLeft
// 注意:当一个元素有框架: 自动设置左偏移量和框架左面
// offsetLeft:可以理解成盒结构(CSS中的盒子结构)的左边离定义好的外盒框架左边的距离
// are the same in Safari causing offset.left to incorrectly be 0
// 是相同的在Safari中引起offset.left错误变成0
return {
top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
};
}, offsetParent: function() {
return this.map(function() {
var offsetParent = this.offsetParent || document.documentElement;
while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || document.documentElement;
});
}
});
fadeTo: function( speed, to, easing, callback ) { // show any hidden elements after setting opacity to 0
// 展示任意隐藏的元素在将opacity(不透明性)设置为0后
return this.filter( isHidden ).css( "opacity", 0 ).show() // animate to the value specified
// 动画指定的值
.end().animate({ opacity: to }, speed, easing, callback );
},
animate: function( prop, speed, easing, callback ) {
var empty = jQuery.isEmptyObject( prop ),
optall = jQuery.speed( speed, easing, callback ),
doAnimation = function() {
// Operate on a copy of prop so per-property easing won't be lost
// 在属性的副本上进行操作,可以让 per-property(每个属性)easing不会丢失
// easing :应该是JQ用到的一种方法(此处待修改)
var anim = Animation( this, jQuery.extend( {}, prop ), optall );
doAnimation.finish = function() {
anim.stop( true );
};
// Empty animations, or finishing resolves immediately
if ( empty || jQuery._data( this, "finish" ) ) {
anim.stop( true );
}
};
doAnimation.finish = doAnimation; return empty || optall.queue === false ?
this.each( doAnimation ) :
this.queue( optall.queue, doAnimation );
},
stop: function( type, clearQueue, gotoEnd ) {
var stopQueue = function( hooks ) {
var stop = hooks.stop;
delete hooks.stop;
stop( gotoEnd );
}; if ( typeof type !== "string" ) {
gotoEnd = clearQueue;
clearQueue = type;
type = undefined;
}
if ( clearQueue && type !== false ) {
this.queue( type || "fx", [] );
} return this.each(function() {
var dequeue = true,
index = type != null && type + "queueHooks",
timers = jQuery.timers,
data = jQuery._data( this ); if ( index ) {
if ( data[ index ] && data[ index ].stop ) {
stopQueue( data[ index ] );
}
} else {
for ( index in data ) {
if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
stopQueue( data[ index ] );
}
}
} for ( index = timers.length; index--; ) {
if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
timers[ index ].anim.stop( gotoEnd );
dequeue = false;
timers.splice( index, 1 );
}
} // start the next in the queue if the last step wasn't forced
// 开始在队列内的下一个如果最后一步没被强制执行
// timers currently will call their complete callbacks, which will dequeue
// 计时器现在将会访问他们完成的回调,将列出
// but only if they were gotoEnd
// 但是只有在他们转入结束的时候
if ( dequeue || !gotoEnd ) {
jQuery.dequeue( this, type );
}
});
},
finish: function( type ) {
if ( type !== false ) {
type = type || "fx";
}
return this.each(function() {
var index,
data = jQuery._data( this ),
queue = data[ type + "queue" ],
hooks = data[ type + "queueHooks" ],
timers = jQuery.timers,
length = queue ? queue.length : 0; // enable finishing flag on private data
// 使完成标志能够在私有数据上
data.finish = true; // empty the queue first
// 实现让队列为空
jQuery.queue( this, type, [] ); if ( hooks && hooks.cur && hooks.cur.finish ) {
hooks.cur.finish.call( this );
} // look for any active animations, and finish them
// 寻找任意有效的动画,并且完成他们
for ( index = timers.length; index--; ) {
if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
timers[ index ].anim.stop( true );
timers.splice( index, 1 );
}
} // look for any animations in the old queue and finish them
// 寻找任意动画在旧的队列里并且完成他们
for ( index = 0; index < length; index++ ) {
if ( queue[ index ] && queue[ index ].finish ) {
queue[ index ].finish.call( this );
}
} // turn off finishing flag
// 关闭完成的标志
delete data.finish;
});
}
});// Generate parameters to create a standard animation
// 生成参数为了创建一个标准的动画
function genFx( type, includeWidth ) {
var which,
attrs = { height: type },
i = 0; // if we include width, step value is 1 to do all cssExpand values,
// if we don't include width, step value is 2 to skip over Left and Right
includeWidth = includeWidth? 1 : 0;
for( ; i < 4 ; i += 2 - includeWidth ) {
which = cssExpand[ i ];
attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
} if ( includeWidth ) {
attrs.opacity = attrs.width = type;
} return attrs;
}// Generate shortcuts for custom animations
// 为自动以动画制作形成快捷方式
jQuery.each({
slideDown: genFx("show"),
slideUp: genFx("hide"),
slideToggle: genFx("toggle"),
fadeIn: { opacity: "show" },
fadeOut: { opacity: "hide" },
fadeToggle: { opacity: "toggle" }
}, function( name, props ) {
jQuery.fn[ name ] = function( speed, easing, callback ) {
return this.animate( props, speed, easing, callback );
};
});jQuery.speed = function( speed, easing, fn ) {
var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
complete: fn || !fn && easing ||
jQuery.isFunction( speed ) && speed,
duration: speed,
easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
}; opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; // normalize opt.queue - true/undefined/null -> "fx"
// 标准化 opt.queue - 真/未定义/空值 -> "fx"
if ( opt.queue == null || opt.queue === true ) {
opt.queue = "fx";
} // Queueing
// 排队技术
opt.old = opt.complete; opt.complete = function() {
if ( jQuery.isFunction( opt.old ) ) {
opt.old.call( this );
} if ( opt.queue ) {
jQuery.dequeue( this, opt.queue );
}
}; return opt;
};jQuery.easing = {
linear: function( p ) {
return p;
},
swing: function( p ) {
return 0.5 - Math.cos( p*Math.PI ) / 2;
}
};jQuery.timers = [];
jQuery.fx = Tween.prototype.init;
jQuery.fx.tick = function() {
var timer,
timers = jQuery.timers,
i = 0; fxNow = jQuery.now(); for ( ; i < timers.length; i++ ) {
timer = timers[ i ];
// Checks the timer has not already been removed
// 检查计时器还没被移除
if ( !timer() && timers[ i ] === timer ) {
timers.splice( i--, 1 );
}
} if ( !timers.length ) {
jQuery.fx.stop();
}
fxNow = undefined;
};jQuery.fx.timer = function( timer ) {
if ( timer() && jQuery.timers.push( timer ) ) {
jQuery.fx.start();
}
};jQuery.fx.interval = 13;jQuery.fx.start = function() {
if ( !timerId ) {
timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
}
};jQuery.fx.stop = function() {
clearInterval( timerId );
timerId = null;
};jQuery.fx.speeds = {
slow: 600,
fast: 200,
// Default speed
// 规定速度
_default: 400
};// Back Compat <1.8 extension point
// 返回兼容性小于1.8的扩展点
jQuery.fx.step = {};if ( jQuery.expr && jQuery.expr.filters ) {
jQuery.expr.filters.animated = function( elem ) {
return jQuery.grep(jQuery.timers, function( fn ) {
return elem === fn.elem;
}).length;
};
}
jQuery.fn.offset = function( options ) {
if ( arguments.length ) {
return options === undefined ?
this :
this.each(function( i ) {
jQuery.offset.setOffset( this, options, i );
});
} var docElem, win,
box = { top: 0, left: 0 },
elem = this[ 0 ],
doc = elem && elem.ownerDocument; if ( !doc ) {
return;
} docElem = doc.documentElement; // Make sure it's not a disconnected DOM node
// 确定它不是一个断开的DOM节点
if ( !jQuery.contains( docElem, elem ) ) {
return box;
} // If we don't have gBCR, just use 0,0 rather than error
// 如果我们没有gBCR,就使用0,0而不是error
// BlackBerry 5, iOS 3 (original iPhone)
// BlackBerry(黑莓) 5,iOS 3(最初的iPhone)
if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
box = elem.getBoundingClientRect();
}
win = getWindow( doc );
return {
top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ),
left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
};
};
jQuery.ready.promise().done( fn ); return this;
}, slice: function() {
return this.pushStack( core_slice.apply( this, arguments ) );
}, first: function() {
return this.eq( 0 );
}, last: function() {
return this.eq( -1 );
}, eq: function( i ) {
var len = this.length,
j = +i + ( i < 0 ? len : 0 );
return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
}, map: function( callback ) {
return this.pushStack( jQuery.map(this, function( elem, i ) {
return callback.call( elem, i, elem );
}));
}, end: function() {
return this.prevObject || this.constructor(null);
}, // For internal use only.
// 只是为了内部使用
// Behaves like an Array's method, not like a jQuery method.
push: core_push,
sort: [].sort,
splice: [].splice
};// Give the init function the jQuery prototype for later instantiation
// 把init函数给jQuery原型为稍后的实例化
jQuery.fn.init.prototype = jQuery.fn;jQuery.extend = jQuery.fn.extend = function() {
var src, copyIsArray, copy, name, options, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false; // Handle a deep copy situation
// 处理深度拷贝情况
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
// 忽略布尔值和服务对象
i = 2;
} // Handle case when target is a string or something (possible in deep copy)
// 处理事件当对象是一个字符串或其他东西
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
} // extend jQuery itself if only one argument is passed
// 如果只有一个参数是通过的,扩展jQuery本身
if ( length === i ) {
target = this;
--i;
} for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
// 只能处理 non-null/未定义 的值
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
// 扩展基本对象
for ( name in options ) {
src = target[ name ];
copy = options[ name ]; // Prevent never-ending loop
// 防止无休止的循环
if ( target === copy ) {
continue;
} // Recurse if we're merging plain objects or arrays
// 递归,如果我们简单地合并对象或数组
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : []; } else {
clone = src && jQuery.isPlainObject(src) ? src : {};
} // Never move original objects, clone them
// 从不移动原始的对象,克隆他们
target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values
// 不要提供未定义的值
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
} // Return the modified object
// 返回被修改的对象
return target;
};jQuery.extend({
noConflict: function( deep ) {
if ( window.$ === jQuery ) {
window.$ = _$;
} if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
} return jQuery;
}, // Is the DOM ready to be used? Set to true once it occurs.
// DOM文档是否准备被使用?一旦发生就设置为真。
isReady: false, // A counter to track how many items to wait for before
// 一个计数器跟踪多少项目等待
// the ready event fires. See #6781
// 准备好的事件。请看#6781
readyWait: 1, // Hold (or release) the ready event
// 控制(或者释放)准备的事件
holdReady: function( hold ) {
if ( hold ) {
jQuery.readyWait++;
} else {
jQuery.ready( true );
}
}, // Handle when the DOM is ready
// 操纵DOM文档何时被准备
ready: function( wait ) { // Abort if there are pending holds or we're already ready
// 终止如果有待定的holds或者已经准备好了
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
return;
} // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
// 确认body结构存在,至少,免得IE有点overzealous(暂时译为:过分热心)标签#5443
if ( !document.body ) {
return setTimeout( jQuery.ready );
} // Remember that the DOM is ready
// 记住文件对象模型(DOM)正在准备
jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --jQuery.readyWait > 0 ) {
return;
} // If there are functions bound, to execute
// 如果有绑定的函数,执行它
readyList.resolveWith( document, [ jQuery ] ); // Trigger any bound ready events
// 触发任意绑定的准备事件
if ( jQuery.fn.trigger ) {
jQuery( document ).trigger("ready").off("ready");
}
}, // See test/unit/core.js for details concerning isFunction.
// 看test/unit/core.js这个文件,为了详述concerning isFunction(暂时译为:关系到的函数)
// Since version 1.3, DOM methods and functions like alert
// 自从1.3版本,DOM方法和函数像警报
// aren't supported. They return false on IE (#2968).
// 不被支持。它们在IE返回错误(#2968)
isFunction: function( obj ) {
return jQuery.type(obj) === "function";
}, isArray: Array.isArray || function( obj ) {
return jQuery.type(obj) === "array";
}, isWindow: function( obj ) {
return obj != null && obj == obj.window;
}, isNumeric: function( obj ) {
return !isNaN( parseFloat(obj) ) && isFinite( obj );
}, type: function( obj ) {
if ( obj == null ) {
return String( obj );
}
return typeof obj === "object" || typeof obj === "function" ?
class2type[ core_toString.call(obj) ] || "object" :
typeof obj;
}, isPlainObject: function( obj ) {
// Must be an Object.
// 必须是对象
// Because of IE, we also have to check the presence of the constructor property.
// 由于IE的原因,我们必须检查存在的构造器属性
// Make sure that DOM nodes and window objects don't pass through, as well
// 还有确保DOM节点和window对象不被传进来
if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
return false;
} try {
// Not own constructor property must be Object
// 拥有的构造器属性不一定是对象
if ( obj.constructor &&
!core_hasOwn.call(obj, "constructor") &&
!core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
return false;
}
} catch ( e ) {
// IE8,9 Will throw exceptions on certain host objects #9897
// IE8,9将会扔出例外在某些主机对象#9897
return false;
} // Own properties are enumerated firstly, so to speed up,
// 首先列举拥有的属性,为了加速,只判断最后一个
// if last one is own, then all properties are own.
// 如果含有最后一个属性,那么含有所有的属性
var key;
for ( key in obj ) {} return key === undefined || core_hasOwn.call( obj, key );
//遍历对象中的变量key返回key没有找到
}, isEmptyObject: function( obj ) {
var name;
for ( name in obj ) {
return false;
}
return true;
}, error: function( msg ) {
throw new Error( msg );
}, // data: string of html
// 数据:HTML的字符串
// context (optional): If specified, the fragment will be created in this context, defaults to document
// 上下文内容(任意的):如果被指定,片段会被创建到该上下文上,默认是创建在文档上
// keepScripts (optional): If true, will include scripts passed in the html string
// 保留脚本(可选的):如果为真,包含的脚本在html字符串中会通过
parseHTML: function( data, context, keepScripts ) {// 解析HTML字符串,生成相应的dom元素并返回
if ( !data || typeof data !== "string" ) {
return null;
}
if ( typeof context === "boolean" ) {// 如果context是布尔类型,则将context的值赋给keepScripts,并将context设置为假
keepScripts = context;
context = false;
}
context = context || document; var parsed = rsingleTag.exec( data ),
scripts = !keepScripts && []; // Single tag
// 单独的标签
if ( parsed ) {
return [ context.createElement( parsed[1] ) ];
} parsed = jQuery.buildFragment( [ data ], context, scripts );
if ( scripts ) {
jQuery( scripts ).remove();
}
return jQuery.merge( [], parsed.childNodes );
}, parseJSON: function( data ) {
// Attempt to parse using the native JSON parser first
// 首相尝试解析正在使用的本地的JSON解析程序
if ( window.JSON && window.JSON.parse ) {
return window.JSON.parse( data );
} if ( data === null ) {
return data;
} if ( typeof data === "string" ) { // Make sure leading/trailing whitespace is removed (IE can't handle it)
// 确认开头、末尾空白符号已被移走(IE无法操作)
data = jQuery.trim( data ); if ( data ) {
// Make sure the incoming data is actual JSON
// 确认输入的数据现在的JSON数据
// Logic borrowed from http://json.org/json2.js
// 从http://json.org/json2.js中借用逻辑
if ( rvalidchars.test( data.replace( rvalidescape, "@" )
.replace( rvalidtokens, "]" )
.replace( rvalidbraces, "")) ) { return ( new Function( "return " + data ) )();
}
}
} jQuery.error( "Invalid JSON: " + data );
}, // Cross-browser xml parsing
// 跨浏览器的xml解析
parseXML: function( data ) {
var xml, tmp;
if ( !data || typeof data !== "string" ) {
return null;//如果传入的数据类型不是字符串,返回空值。
}
try {
if ( window.DOMParser ) { // Standard(标准)
tmp = new DOMParser();
xml = tmp.parseFromString( data , "text/xml" );
} else { // IE
xml = new ActiveXObject( "Microsoft.XMLDOM" );
xml.async = "false";
xml.loadXML( data );
}
} catch( e ) {
xml = undefined;
}
if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
jQuery.error( "Invalid XML: " + data );
}
return xml;
}, noop: function() {}, // Evaluates a script in a global context
// 在全局上下文中执行脚本
// Workarounds based on findings by Jim Driscoll
// 工作环境基于Jim Driscoll的发现
// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
// 上面提到那人的一篇文章
globalEval: function( data ) {
if ( data && jQuery.trim( data ) ) {
// We use execScript on Internet Explorer
// 我们使用execScript函数在IE浏览器
// eval() 与 window.execScript()作用基本相同
// 说一点这两个区别: 在FireFox下window.execScript()函数不能运行,而eval()在两个浏览器下都能正常运行
// eval的作用:把一段字符串传递给JS解析器,由Javascript解析器将这段字符串解析成Javascript
// We use an anonymous function so that context is window
// 我们使用一个匿名函数为了内容在window
// rather than jQuery in Firefox
// 相当于jQuery在Firefox(火狐浏览器)
( window.execScript || function( data ) {
window[ "eval" ].call( window, data );
} )( data );
}
}, // Convert dashed to camelCase; used by the css and data modules
// 迅速转换为“骆驼拼写法”;使用CSS和数据模块
// 骆驼拼写法:依靠单词的大小写拼写一个复合词的做法,比如比如,backColor(背景色)
// 这种拼写法在正规的英语中是不允许的,但是在编程语言和商业活动中却大量使用。
// 比如,sony公司的畅销游戏机PlayStation,play和station两个词的词首字母都是大写的。
// 这样做的好处是可以用一连串的描述性单词来表述一个新的含义——因为中间没有空格,可以被视为一个新词(组),而不是一个短语。
// Microsoft forgot to hump their vendor prefix (#9572)
// 微软忘记驼峰他们的供应商前缀(#9572)
camelCase: function( string ) {
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
}, nodeName: function( elem, name ) {
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
},
// args is for internal usage only
// args(暂时译为:参数列表)仅供内部使用
each: function( obj, callback, args ) {
var value,
i = 0,
length = obj.length,
isArray = isArraylike( obj ); if ( args ) {
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.apply( obj[ i ], args ); if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) {
value = callback.apply( obj[ i ], args ); if ( value === false ) {
break;
}
}
} //一个特殊的,快速的,实例用于每个大部分普通应用
} else {
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) {
value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) {
break;
}
}
}
} return obj;
},
// Use native String.trim function wherever possible
// 在任何可能的地方,使用本地的String.trim函数
trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
function( text ) {
return text == null ?
"" :
core_trim.call( text );
} :
// Otherwise use our own trimming functionality
// 否则使用我们自己的微调功能
function( text ) {
return text == null ?
"" :
( text + "" ).replace( rtrim, "" );
},
// results is for internal usage only
// 结果仅供内部使用
makeArray: function( arr, results ) {
var ret = results || []; if ( arr != null ) {
if ( isArraylike( Object(arr) ) ) {
jQuery.merge( ret,
typeof arr === "string" ?
[ arr ] : arr
);
} else {
core_push.call( ret, arr );
}
} return ret;
}, inArray: function( elem, arr, i ) {
var len; if ( arr ) {
if ( core_indexOf ) {
return core_indexOf.call( arr, elem, i );
} len = arr.length;
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; for ( ; i < len; i++ ) {
// Skip accessing in sparse arrays
// 跳过访问稀少数组
if ( i in arr && arr[ i ] === elem ) {
return i;
}
}
} return -1;
}, merge: function( first, second ) {
var l = second.length,
i = first.length,
j = 0; if ( typeof l === "number" ) {
for ( ; j < l; j++ ) {
first[ i++ ] = second[ j ];
}
} else {
while ( second[j] !== undefined ) {
first[ i++ ] = second[ j++ ];
}
} first.length = i; return first;
}, grep: function( elems, callback, inv ) {
var retVal,
ret = [],
i = 0,
length = elems.length;
inv = !!inv;
// Go through the array, only saving the items that pass the validator function
// 通过数组,仅保留通过校验函数的项目
for ( ; i < length; i++ ) {
retVal = !!callback( elems[ i ], i );
if ( inv !== retVal ) {
ret.push( elems[ i ] );
}
} return ret;
},
// arg is for internal usage only
// arg仅供内部使用
此号id【0u010156773】防忘
map: function( elems, callback, arg ) {
var value,
i = 0,
length = elems.length,
isArray = isArraylike( elems ),
ret = [];
// Go through the array, translating each of the items to their
// 转化每个项目,并赋值给数组。
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg ); if ( value != null ) {
ret[ ret.length ] = value;
}
}
// Go through every key on the object,
// 通过每个对象的关键
} else {
for ( i in elems ) {
value = callback( elems[ i ], i, arg ); if ( value != null ) {
ret[ ret.length ] = value;
}
}
}
// Flatten any nested arrays
// 拼接任意嵌套数组
return core_concat.apply( [], ret );
}, // A global GUID counter for objects
// 一个关于对象的全局GUID计数器
// GUID:是Globally Unique Identifier(全局唯一标识符)的缩写
guid: 1, // Bind a function to a context, optionally partially applying any
// arguments.
// 函数绑定到上下内容,任意地、部分地应用任何参数
proxy: function( fn, context ) {
var args, proxy, tmp; if ( typeof context === "string" ) {
tmp = fn[ context ];
context = fn;
fn = tmp;
} // Quick check to determine if target is callable, in the spec
// this throws a TypeError, but we will just return undefined.
// 快速检查,判断目标是否可访问,在规范中,这将引发一个TypeError(类型错误),我们将仅返回未定义的。
if ( !jQuery.isFunction( fn ) ) {
return undefined;
} // Simulated bind
// 模拟绑定
args = core_slice.call( arguments, 2 );
proxy = function() {
return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
}; // Set the guid of unique handler to the same of original handler, so it can be removed
// 把guid唯一的处理程序设置成语最初的处理程序相同,以便它能被删除
proxy.guid = fn.guid = fn.guid || jQuery.guid++; return proxy;
}, // Multifunctional method to get and set values of a collection
// 多种函数方法得到和设置一个集合的值
// The value/s can optionally be executed if it's a function
// 如果它是一个函数,单个或多个数值可以有选择的被执行。
access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
var i = 0,
length = elems.length,
bulk = key == null; // Sets many values
// 设置很多值
if ( jQuery.type( key ) === "object" ) {
chainable = true;
for ( i in key ) {
jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
} // Sets one value
// 设置一个值
} else if ( value !== undefined ) {
chainable = true; if ( !jQuery.isFunction( value ) ) {
raw = true;
} if ( bulk ) {
// Bulk operations run against the entire set
// 整体设置之前,选项批量运行。
if ( raw ) {
fn.call( elems, value );
fn = null; // ...except when executing function values
// ...除了当执行函数值时
} else {
bulk = fn;
fn = function( elem, key, value ) {
return bulk.call( jQuery( elem ), value );
};
}
} if ( fn ) {
for ( ; i < length; i++ ) {
fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
}
}
} return chainable ?
elems : // Gets
// 得到(这里翻译估计不准确)
bulk ?
fn.call( elems ) :
length ? fn( elems[0], key ) : emptyGet;
}, now: function() {
return ( new Date() ).getTime();
}
});jQuery.ready.promise = function( obj ) {
if ( !readyList ) { readyList = jQuery.Deferred(); // Catch cases where $(document).ready() is called after the browser event has already occurred.
// 在浏览器的事件已经发生后,捕捉$(document).ready()被访问的事例
// we once tried to use readyState "interactive" here, but it caused issues like the one
// 我们这里曾经尝试使用readyState "interactive"(相互影响),不是它引起了问题类似ChrisS发现的
// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
// 问题网址:http://bugs.jquery.com/ticket/12282#comment:15
//readyState:HTTP请求的状态。属性的值从0开始,直到接收到完整的HTTP响应,这个值增加到4。
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
// 异步处理,为了允许脚本有机会做延迟准备
setTimeout( jQuery.ready ); // Standards-based browsers support DOMContentLoaded
// 基于标准的浏览器都支持DOMContentLoaded
} else if ( document.addEventListener ) {
// Use the handy event callback
// 使用方便的事件回调
document.addEventListener( "DOMContentLoaded", completed, false ); // A fallback to window.onload, that will always work
window.addEventListener( "load", completed, false ); // If IE event model is used
// 如果IE时间模块被使用
} else {
// Ensure firing before onload, maybe late but safe also for iframes
// 在onload(暂时译为:加载)前确保解雇,也许迟了,但是对于内联框架也是安全的
// iframe:元素会创建包含另外一个文档的内联框架(即行内框架)
// 在 HTML 4.1 Strict DTD 和 XHTML 1.0 Strict DTD 中,不支持 iframe 元素。
document.attachEvent( "onreadystatechange", completed ); // A fallback to window.onload, that will always work
// 对于window.onload,fallback(暂时译为:回退)将总会运行
window.attachEvent( "onload", completed ); // If IE and not a frame
// 如果是IE并且不是一个框架
// continually check to see if the document is ready
// 持续监测,观察文档是否已经就绪
var top = false; try {
top = window.frameElement == null && document.documentElement;
} catch(e) {} if ( top && top.doScroll ) {
(function doScrollCheck() {
if ( !jQuery.isReady ) { try {
// Use the trick by Diego Perini
// 使用Diego Perini的诀窍
// Diego Perini:本人才疏学浅,不知道此人是谁,不过在这里提起这人,想来这人也不简单
// Google找到相关资料;JSMentors(JS顾问?)Diego Perini 网址:http://jsmentors.com/Diego-Perini.html
// http://javascript.nwbox.com/IEContentLoaded/
// 上面这个地址是此人个人主页上的一篇文章:讲的是IE方面的内容
top.doScroll("left");
} catch(e) {
return setTimeout( doScrollCheck, 50 );
} // detach all dom ready events
// 断开所有DOM准备事件
detach(); // and execute any waiting functions
// 并且执行任何等待的函数
jQuery.ready();
}
})();
}
}
}
return readyList.promise( obj );
};// Populate the class2type map
// 填充class2type地图
// class2type:不知道怎么解释
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
});function isArraylike( obj ) {
var length = obj.length,
type = jQuery.type( obj ); if ( jQuery.isWindow( obj ) ) {
return false;
} if ( obj.nodeType === 1 && length ) {
return true;
} return type === "array" || type !== "function" &&
( length === 0 ||
typeof length === "number" && length > 0 && ( length - 1 ) in obj );
}
// 上面这个isArraylike(obj)最后的return非常值得一看,小弟也看了很多代码,这么长的return确实没见过,而且还发现了好多不懂之处。
/*
来自(functionsub)前辈的解释,原样照搬。
return type === "array" || type !== "function" &&
( length === 0 ||
typeof length === "number" && length > 0 && ( length - 1 ) in obj );
若type==='array'直接返回true
若type!=='array'的话,如果type!=='function'为true的话开始判断括号里的内容,否则整体返回false
括号里的内容如果length===0为true若括号里整体为true,整体返回true
若length===0为false,判断typeof length==='number',如果为flase,整体返回false
如果typeof length==='number',如果为true,判断length>0,如果为false,整体返回false
如果length>0为true,判断( length - 1 ) in obj,这话的意思就是如果是类数组的对象,
其结构肯定是{0:'aaa',1:'bbb',length:2}这样的key值为数字的,所以如果是类数组对象,判断在obj里是否能找到length-1这样的key,
如果找到,整体返回true,否则整体返回false
in就是判断一个key是否在一个obj里。比如var obj = {a:'111'},'a' in obj为true,'b' in obj为false
*/
// All jQuery objects should point back to these
// 所有的jQuery对象应该指向这些
rootjQuery = jQuery(document);
// String to Object options format cache
// 字符串对象选择缓存形式
var optionsCache = {};
// Convert String-formatted options into Object-formatted ones and store in cache
// 把字符串格式的选项转换为对象格式的选项,并且存储在缓存中。
function createOptions( options ) {
var object = optionsCache[ options ] = {};
jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
object[ flag ] = true;
});
return object;
}
此号id【u010156773】防忘
/*
* Create a callback list using the following parameters:
* 创建一个回调列表,使用一下参数:
* options: an optional list of space-separated options that will change how
* the callback list behaves or a more traditional option object
* 选项:一个带有空间分割的可选择列表,将会改变回调列表行为或者一个传统的选项对象
* By default a callback list will act like an event callback list and can be
* "fired" multiple times.
* 默认情况下,一个回调列表会采取一个类似事件回调列表的行为,并且能够多次"fired"(fired这里暂时译为:被炒鱿鱼)
* Possible options:
* 可能的选项:
* once: will ensure the callback list can only be fired once (like a Deferred)
* 一次: 将确保回调列表只能发生一次,例如一个延期(fire在这文档大量出现,意思不同,真的不好翻译)
* memory: will keep track of previous values and will call any callback added
* after the list has been fired right away with the latest "memorized"
* values (like a Deferred)
* 内存: 将持续监测以前的值,并且将调用任何被添加的回调
* 在立刻用最后被记忆的值(比如一个延期)清理列表之后
* unique: will ensure a callback can only be added once (no duplicate in the list)
* 唯一: 将确保一个回调仅能被添加一次(在列表中没有副本)
* stopOnFalse: interrupt callings when a callback returns false
* 发生错误停止: 当一个回调返回错误时,中断调用
*/
jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed
// 如果需要,转换选项从字符串格式到对象格式。
// (we check in cache first)
// (我们首先检测存储器)
// Cache:高速缓冲存储器
// 借用以下文章的一些注释,要不下面的一堆fire我就彻底无法解释了。
// 文章地址:http://www.cnblogs.com/littledu/articles/2811728.html
// 关于下面options的解释:这里设计为一个对象,这一步就是先判断下options缓存里有没这个对象存在,有直接拿来用,
// 没有则通过createOptions方法将传过来的字符串转化为一个对象,对象形式如下:
options = typeof options === "string" ?
( optionsCache[ options ] || createOptions( options ) ) :
jQuery.extend( {}, options ); var // Flag to know if list is currently firing(标记知道如果列表是firing,这里的firing是下面的一个变量)
firing,
// Last fire value (for non-forgettable lists)最后fire的值(非遗忘列表)
memory,
// Flag to know if list was already fired(标记知道如果列表是fired,这里的fired是下面的一个变量)
fired,
// End of the loop when firing(当是这个firing时,结束循环)
firingLength,
// Index of currently firing callback (modified by remove if needed)当前firing回调的索引(如果需要删除修改)
firingIndex,
// First callback to fire (used internally by add and fireWith)
// 对于fire的第一个回调(内部使用添加或者解雇)
firingStart,
// Actual callback list
// 现行的回调列表
list = [],
// Stack of fire calls for repeatable lists
// 在可重复的列表中,跟踪fire调用
stack = !options.once && [],
// Fire callbacks
// Fire回调
fire = function( data ) {
memory = options.memory && data; //当$.Callbacks('memory')时,保存data,data为数组[context,args]
fired = true; //表示已经执行,用于表示队列里的回调已经执行过一次
firingIndex = firingStart || 0; //执行队列的下标,相当于普通循环的i,当$.Callbacks('memory')时,需设置firingIndex
firingStart = 0; //重置队列起始值
firingLength = list.length; //保存队列的长度
firing = true; //标示正在执行中
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
// 遍历回调数组,执行每一个回调,当回调返回false且有传入stopOnFalse时,
// 也就是$.Callbacks('stopOnFalse'),中止后面回调的执行。
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
memory = false; // To prevent further calls using add阻止更远的调用使用添加
break;
}
}
firing = false; //函数执行完后,将执行中的标示设为false
if ( list ) {
if ( stack ) { //执行完回调后,看一下stack是否有回调,如果有拿出来执行
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}
},
// Actual Callbacks object
// 现在的回调对象
self = {
// Add a callback or a collection of callbacks to the list
// 给列表增加回调或者一个回调的集合
add: function() {
if ( list ) {
// First, we save the current length
// 首先,我们保存现在的长度
var start = list.length;
(function add( args ) {
jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg );
if ( type === "function" ) {
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
} else if ( arg && arg.length && type !== "string" ) {
// Inspect recursively
// 递归观测
add( arg );
}
});
})( arguments );
// Do we need to add the callbacks to the
// current firing batch?
// 我们需要给当前的firing堆中添加回调吗?
if ( firing ) {
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
// 使用内容,如果我们不是firing,那么我们应该马上调用
} else if ( memory ) {
firingStart = start;
fire( memory );
}
}
return this;
},
// Remove a callback from the list
// 从列表冲移除一个回调
remove: function() {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );
// Handle firing indexes
// 管理firing索引
if ( firing ) {
if ( index <= firingLength ) {
firingLength--;
}
if ( index <= firingIndex ) {
firingIndex--;
}
}
}
});
}
return this;
},
// Check if a given callback is in the list.
// 检测给定的回调是否在列表中
// If no argument is given, return whether or not list has callbacks attached.
// 如果什么参数也没给,无论列表有没有回调参与都返回
has: function( fn ) {
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
},
// Remove all callbacks from the list
// 从列表中移除所有的回调
empty: function() {
list = [];
return this;
},
// Have the list do nothing anymore
// 列表消失了
disable: function() {
list = stack = memory = undefined;
return this;
},
// Is it disabled?
// 它是禁用的吗?
disabled: function() {
return !list;
},
// Lock the list in its current state
// 锁定列表,保持它当前的状态
lock: function() {
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},
// Is it locked?
// 它被锁住了吗?
locked: function() {
return !stack;
},
// Call all callbacks with the given context and arguments
// 用给定的内容和参数调用所有的回调
fireWith: function( context, args ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
if ( list && ( !fired || stack ) ) {
if ( firing ) {
stack.push( args );
} else {
fire( args );
}
}
return this;
},
// Call all the callbacks with the given arguments
// 用给定的参数调用所有的回调
fire: function() {
self.fireWith( this, arguments );
return this;
},
// To know if the callbacks have already been called at least once
// 为了知道回调是否已经被调用了至少一次
fired: function() {
return !!fired;
}
}; return self;
};
jQuery.extend({ Deferred: function( func ) {
var tuples = [
// action, add listener, listener list, final state
// 动作、添加监听(我翻译为监听,那位前辈翻译为回调函数)、监听列表、最终状态
// 以下添加了关于jQuery.Deferred的解释(感觉解释的很详细,所以就写了上来)
// 原文是在jQuery1.8.2的源码上进行分析。此版本是1.9.1版本,与原文代码稍有一些变化,请注意下。
// 摘自Lovesueee的博客文章:http://www.cnblogs.com/lovesueee/archive/2012/10/18/2730287.html
// 数据元组集
// 每个元组分别包含一些与当前deferred相关的信息:
// 分别是:触发回调函数列表执行(函数名),添加回调函数(函数名),
// 回调函数列表(jQuery.Callbacks对象),deferred最终状态(第三组数据除外)
// 总体而言,三个元组会有对应的三个callbacklist对应于doneList, failList, processList [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],
// deferred的状态,分为三种:pending(初始状态), resolved(解决状态), rejected(拒绝状态)
state = "pending",
// promise对象,主要有两点作用:
// 1. 在初始化deferred对象时,promise对象里的方法都会被extend到deferred中去,作为引用
// 2. 那么,生成的deferred对象里必然引用了promise对象的promise方法,所以当调用deferred.promise()时,
// deferred对象会通过闭包返回promise对象,这就是所谓的受限制的deferred对象(用deferred2表示),因为相比之前,
// 返回的deferred2不在拥有resolve(With), reject(With),
// notify(With)这些能改变deferred对象状态并且执行callbacklist的方法了
promise = {
// 返回闭包里的内部state(外部只读)
state: function() {
return state;
},
// 同时在doneList和failList的list里添加回调函数(引用)
// 那么不论deferred最终状态是resolved还是rejected, 回调函数都会被执行,这就是所谓的always
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
// jQuery.then()会创建一个新的受限制的deferred对象
then: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;
// 创建新的受限制的deferred对象(称作newDeferrred),并返回
// 利用返回的deferred对象就可以做很多事了,你懂的
return jQuery.Deferred(function( newDefer ) {
jQuery.each( tuples, function( i, tuple ) {
var action = tuple[ 0 ],
fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
// deferred[ done | fail | progress ] for forwarding actions to newDefer
// 此行是翻译:为了将来关于新延期的行动,延期[ 被做 | 失败 | 进行 ] defer这里翻译为:延期
// 分别为deferred的三个callbacklist添加回调函数,根据fn的是否是函数,分为两种情况:
// 1.不是函数的情况(如值为undefined或者null等),直接链接到newDeferred的resolve(reject,notify)方法,也就是说
// newDeferrred的执行依赖外层的调用者deferred的状态或者说是执行动作(resolve还是reject或者是notify)
// 此时deferred.then()相当于将自己的callbacklist和newDeferred的callbacklist连接起来了,故可以在newDeferred
// 中大做文章
// 2.是函数的情况,根据返回值(称作returnReferred)是否是deferred对象,又可以分为两种情况:
// 2.1 返回值是deferred对象,那么在returnReferred对象的三个回调函数列表中添加newDeferred的resolve(reject,notify)方法
// 也就是说newDeferrred的执行依赖returnDeferred的状态
// 2.2 返回值不是deferred对象,那么将返回值returned作为newDeferred的参数并将从外层deferred那边的上下文环境作为newDeferred
// 的执行上下文,然后执行对应的回调函数列表,此时newDeferrred的执行依赖外层的调用者deferred的状态
deferred[ tuple[1] ](function() {
var returned = fn && fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
} else {
newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
}
});
});
fns = null;
}).promise();
},
// Get a promise for this deferred
// 获得这次延期的许可
// If obj is provided, the promise aspect is added to the object
// 如果对象被提供,允许的方面被添加到对象
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
},
// 实际返回的deferred对象
deferred = {}; // Keep pipe for back-compat
// 此行是翻译:保持pipe对于back-compat(这两个单词暂时不会翻译)
// pipe和then引用同一个函数,所以功能是一样的
// 只不过通常的用法是:会用pipe进行filter操作
promise.pipe = promise.then; // Add list-specific methods
// 此行是翻译:添加特性列表方法
// 通过上面定义的数据元组集来扩展一些方法
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add
// 此行是翻译:允许[ 被做 | 失败 | 进行 ] = 添加列表
// 给上面的promise对象添加done,fail,process方法
// 这三个方法分别引用三个不同jQuery.Callbacks对象的add方法(不是同一个引用),
// 那么这三个方法的用途就是向各自的回调函数列表list(各自闭包中)中添加回调函数,互不干扰
promise[ tuple[1] ] = list.add; // Handle state
// 此行是翻译:句柄状态
// 通过stateString有值这个条件,预先向doneList,failList中的list添加三个回调函数
// doneList : [changeState, failList.disable, processList.lock]
// failList : [changeState, doneList.disable, processList.lock]
// changeState 指的是下面首先添加的一个改变deferred对象的匿名函数
// 可以看的出:
//不论deferred对象最终是resolve(还是reject),在首先改变对象状态之后,都会disable另一个函数列表failList(或者doneList)
// 然后lock processList保持其状态,最后执行剩下的之前done(或者fail)进来的回调函数
// 当然了,上述情况processList除外
if ( stateString ) {
list.add(function() {
// state = [ resolved | rejected ]
// 状态 = 【被决定的| 被拒绝的】
state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock
// [ 拒绝列表 | 决定列表 ].disable;; 进行列表.lock
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
} // deferred[ resolve | reject | notify ]
// 此行是翻译:延期【决定|拒绝|通知】
// 给deferred对象添加resolve(With), reject(With), notify(With)方法
// 这三个方法分别引用三个不同jQuery.Callbacks对象的fire方法(不是同一个引用),
// 那么这三个方法的用途就是执行各自的回调函数列表,互不干扰
deferred[ tuple[0] ] = function() {
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
return this;
};
deferred[ tuple[0] + "With" ] = list.fireWith;
}); // Make the deferred a promise
// 此行是翻译:给延期一个允许
// 将上面的promise对象extend进deferred中
promise.promise( deferred ); // Call given func if any
// 此行是翻译:调用任何给定的函数
// 如果调用jQuery.Deferred(func)指定了参数,那么调用func并设置func的上下文和参数均为deferred
// 在jQuery.then()中有用到这一点
if ( func ) {
func.call( deferred, deferred );
} // All done!
// 此行是翻译:全都做完了
// 返回最终的deferred对象
return deferred;
}, // Deferred helper
// 此行是翻译:延期帮助
// 参数:一个(或多个)deferred对象(或其他)
// 当传入的所有deferred对象都resolve或者reject了,执行when()创建的deferred对象
// (称之为whenDeferred)对应的回调函数列表(非deferred对象被认为是resolve了) when: function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
// 首先将arguments伪数组转换为真正的数组
resolveValues = core_slice.call( arguments ),
length = resolveValues.length, // the count of uncompleted subordinates
// 此行是翻译:未完成下级的数量
// 1. 在参数个数等于1的情况下:
// 1.1 如果参数是deferred对象,那么remaining = length, 这是remaining就是1嘛
// 1.2 否则remaining为0
// 2. 在参数不等于1(即等于0或者大于1)的情况:remaining = length
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
// 此行是翻译:主延迟。如果resolvevalues由仅有的一个单独延期组成,那么就使用那个。
// 到这里就可以知道:如果参数个数仅为1个,并且是deferred对象,那么就无需再生成deferred对象
deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values
// 此行是翻译:为了决定值和进行值,更新函数
updateFunc = function( i, contexts, values ) {
// 这里返回一个函数作为一个callback完全是为了创建一个闭包,主要是为了保持i的值
return function( value ) {
// 保存各个deferred执行的上下文,也就是说之后whenDeferred的回调函数的上下文就是一个数组
contexts[ i ] = this;
// 保存各个deferred执行时的参数,之后传递给whenDeferred的回调函数
// 此时values的值有原先的jQuery.when()传进来的参数变为各个deferred执行回调时的参数了,也就是说覆盖了
values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
if( values === progressValues ) {
deferred.notifyWith( contexts, values );
} else if ( !( --remaining ) ) {
// 时机成熟,即所有延迟都resolve,执行whenDeferred的回调函数
deferred.resolveWith( contexts, values );
}
};
}, progressValues, progressContexts, resolveContexts; // add listeners to Deferred subordinates; treat others as resolved
// 此行是翻译:给延迟下级增加监听; 处理其他的作为被决定的
// 如果参数个数大于1,那么就是说有可能存在多个deferred对象
// 这时需要一些条件判断以保证是所有的deferred对象都resolve了,再执行whenDeferred的resolve
// 或者当有一个deferred对象reject了,whenDeferred的reject
if ( length > 1 ) {
progressValues = new Array( length );
progressContexts = new Array( length );
resolveContexts = new Array( length );
for ( ; i < length; i++ ) {
// 如果是deferred对象
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
// 给每个参数(deferred对象)添加最后的回调,用来检查此时的状态
resolveValues[ i ].promise()
// 用于当每一个deferred对象resolve回来,用updateFunc返回的函数检查此时其他deferred对象的状态
// (即此时remaining是否等于0了)
// 如果等于0,则执行whenDeferred的resolve,否则继续等待
.done( updateFunc( i, resolveContexts, resolveValues ) )
// 如果有一个deferred对象reject,whenDeferred将执行reject
.fail( deferred.reject )
.progress( updateFunc( i, progressContexts, progressValues ) );
// 如果不是deferred对象,直接--remaining,视为resolve
} else {
--remaining;
}
}
} // if we're not waiting on anything, resolve the master
// 此行是翻译:如果我们不是正在等待什么,解决主延迟
// 如果此时remaining就等与0了,表示没有什么延迟需要等待,那么立即之行whenDeferred的resolveWith
// 此时resolveContexts为undefined, 这就意味这上下文将为全局的window
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}
// 返回受限制的deferred对象
return deferred.promise();
}
});//那位Lovesueee前辈的解释至此结束。
什么时候整理一个完整的上载上来大家下载下来看更方便。
我也在看jquery1.9.1的源码,自己添加一些理解,比如://匿名自调用函数 (function(){//代码逻辑})();
(function( window, undefined ) {...... //undefied的类型是不与任何类型相等的, 与其他任何类型比较都将返回false. 所以这里才用typeof来比较
//===和!==是全等和全不等, 意为无需转换类型直接比较, 结果相等或不相等
// Support: IE<9
// For `typeof node.method` instead of `node.method !== undefined`
core_strundefined = typeof undefined,......//列举出Object和Array常用的prototype方法的引用, 访问时会方便+快一点
// Save a reference to some core methods
core_concat = core_deletedIds.concat,
core_push = core_deletedIds.push,
core_slice = core_deletedIds.slice,
core_indexOf = core_deletedIds.indexOf,
core_toString = class2type.toString,
core_hasOwn = class2type.hasOwnProperty,
core_trim = core_version.trim,...... // Used for matching numbers
//此正则乍一看很是唬人, 仔细分析一下
//[+-]? 匹配字符串前面会出现+号或-号,且次数为1次或0次。
//(?:\d*\.|) 首先来说,这是一个分组,但是前面的?:指示将不自动名分分组也不捕获文本。\d*\.则是匹配出现0个或多个数字后跟一个.号,|是规则分割符,如果满足该条件则不进行后向匹配。
//\d+ 匹配1个或多个数字
//(?:[eE][+-]?\d+|) 分组情况同上,匹配科学计数法。一个大写或小写e,后跟0个或1个+号或-号,再跟1个或一个以上的数字。
core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,......}(window);