jQuery.fn = jQuery.prototype = {
// The current version of jQuery being used
jquery: core_version,
constructor: jQuery,
*重定向,否则constructor 指向Object
*在下面有用到 jQuery 时, 将用this.constructor 代替.
*这么做的好处是,可维护性强?
init: function( selector, context, rootjQuery ) {
*selector : 我们要选择的dom,或者我们要创建的dom?
*context : dom的父容器节点?筛选范围?
*rootjQuery = $(document) 跟节点的jq包装对象 在外面调用时,实际上传不了这个参数.
var match, elem;
//这个是核心的构造函数,最后的实例长什么样由这个函数决定.
// HANDLE: $(""), $(null), $(undefined), $(false)
if ( !selector ) {*返回原来jq对象
return this;
}
// Handle HTML strings
*匹配字符串
if ( typeof selector === "string" ) {
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {*用来匹配''的情况
// Assume that strings that start and end with <> are HTML and skip the regex check
match = [ null, selector, null ];
* 此处用match 来储存selector是因为与下面的情况保持统一
} else {
match = rquickExpr.exec( selector );
*上面75行定义了,rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
*匹配的情况:$(''),$(#id)
* 其实这里我不太懂,为什么selector 一定会在 match[1] 这个位置上.
}
// Match html or make sure no context is specified for #id
if ( match && (match[1] || !context) ) {
*context 不为空时?
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context;
*如果context传的是jQuery对象,改成原生dom
// scripts is true for back-compat
jQuery.merge( this, jQuery.parseHTML(
match[1],
context && context.nodeType ? context.ownerDocument || context : document,
true
) );
*$.merge函数是用来合并数组的.返回第一个参数.(更改第一个参数)
*最终返回的格式取决于第一个参数
*这里把第二个参数添加到实例对象中.
*merge源码在636行,用来合并数组或者类数组,
*无法合并两个对象(非类数组).
*并且不会出现覆盖.
*
*
*$.parseHTML()实际上是创建dom元素,返回数组.
*第一个参数是字符串
*第二个参数是在哪个document
*第三个参数是能不能创建script,true表示可以.
*
*
// HANDLE: $(html, props)
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
*78行定义了 rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
*匹配的情况 $('')?
*jQuery.isPlainObject() 判断是否为对象,但不能是node元素,也不能是window
*所以此处的意思是,context如果不是dom节点?
*匹配的情况 $('',{title:'123',name:'kakgj'}) 很少用,用来给创建的节点增加属性.
for ( match in context ) {
// Properties of context are called as methods if possible
if ( jQuery.isFunction( this[ match ] ) ) {
this[ match ]( context[ match ] );
*匹配的情况 $('
',{html:'
123
'});这你就很牛逼了.
*这个我服,尽管我可能永远都不会这么用,但开启各种接口,我是真的服!
*在一个原型上定义了方法,我传一个对象,对象名用来调用原型上的方法
*对象值用来当做参数传给该方法.
// ...and otherwise set as attributes
} else {
this.attr( match, context[ match ] );
*匹配的情况 $('
',{title:'123',name:'kakgj'})
}
}
}
return this;
// HANDLE: $(#id)
} else {
elem = document.getElementById( match[2] );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
}
this.context = document;
this.selector = selector;
return this;
*一个标准的jq对象中,通常有context,selector,length,prevObject,以及一堆dom元素
*此时我们发现,这里是不配备prevObject属性的.也容易理解,
}
// HANDLE: $(expr, $(...))
*以下是dom的查找,上面是主要是dom的创建.
*匹配的是 $(('div'),$('father')), 也就是context 传的是jq对象时,context就是这个对象.
*或者是$('div') 也就是没传 context时,默认context 是 rootjQurey
} else if ( !context || context.jquery ) {
return ( context || rootjQuery ).find( selector );
// HANDLE: $(expr, context)
*匹配的是$('div',father);
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor( context ).find( selector );
}
// HANDLE: $(DOMElement)
*这个地方不懂,指的是document?还是html的情况?
* 明白了,,当不是字符串而是dom元素时,
*匹配的情况是 $(div);
*此时返回的对象没有selector,也没有preObject属性
} else if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
// HANDLE: $(function)
*匹配的是$(function(){});
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
*关于jQuery.ready还是另起一篇比较好.
}
*匹配的是$($('div'))
if ( selector.selector !== undefined ) {*可用来判断是否是jq对象
this.selector = selector.selector;
this.context = selector.context;
}
return jQuery.makeArray( selector, this );
*jQuery.makeArray 定义在615行,内部调用的是merge,或者push,
*返回的是第二个参数,如果第二个参数不填入,则第二个参数默认是[];(只传一个参数时,类数组转换成数组)
*所以这句最终是返回this的.
},
*以上就是init构造函数.实际上虽然勉强能看懂,但很明显还有很多含金量没被发掘.
// Start with an empty selector
*selector 默认值
selector: "",
// The default length of a jQuery object is 0
*length 默认值
length: 0,
*这个就是把类数组转变成数组啦
*跟makeArray 相比,这个无法传入两个参数,并且返回的不可能是jq对象.
toArray: function() {
return core_slice.call( this );
},
// Get the Nth element in the matched element set OR
// Get the whole matched element set as a clean array
get: function( num ) {
return num == null ?
// Return a 'clean' array
*还没这么用过, 匹配情况,$('div').get() 相当于 $('div').toArray();
this.toArray() :
// Return just the object
*$('div').get(num) 注意,此时返回的就不是jq对象而是 原生dom元素.
( num < 0 ? this[ this.length + num ] : this[ num ] );
},
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems ) {
*个人觉得这是原型上定义的方法中,除了init之外最有含金量的了.
*主要是解决prevObject 属性,有这个属性才能配合使用end()方法;
*elems 通常应该是一个dom数组.
// Build a new jQuery matched element set
var ret = jQuery.merge( this.constructor(), elems );
*merge就是返回第一个类数组,添加后面类数组的内容.(都是浅克隆)
// Add the old object onto the stack (as a reference)
ret.prevObject = this;*这样就正式设置了prevObject了.
ret.context = this.context;
// Return the newly-formed element set
return ret;
},
// Execute a callback for every element in the matched set.
// (You can seed the arguments with an array of args, but this is
// only used internally.)
each: function( callback, args ) {
return jQuery.each( this, callback, args );
*jQuery.each 方法定义在 源码561行,可以看出整个jq内部调用这个each的频率可是非常高的.
*整个jq最大的亮点就是隐式迭代,用的就是这个each
*返回的是this,假设this就是jq对象的情况,会遍历this当中的所有节点,
*每个节点都会调用一次这个函数,可以传参.
*如果$('div').each(function(index,ele){}),那么callback传的参数是index和ele
*如果$('div').each(function(){a,b,c},[a,b,c]),那么我后面传什么参数就是什么,
*此时this指向的是每个dom元素.我反正是一次都没这么用过.
*如果传参,必须传一个数组.或者类数组..
},
ready: function( fn ) {
// Add the callback
jQuery.ready.promise().done( fn );
*这一部分我大概知道干什么用的,具体怎么实现似乎挺复杂的.还是另起一篇比较好.
return this;
},
slice: function() {
return this.pushStack( core_slice.apply( this, arguments ) );
*这就很巧妙,用了数组的slice方法,以及pushStack方法,
*实现了jq的slice,关键是返回的依然是jq对象.
* 也就是说 pushStack最大的用处实际上是: 把一个dom数组转换成jq对象.
*或者说把一个jq对象转换成另一个jq对象.
*尽管里面用的是merge,但一封装一下,就很方便.
*由此更感觉,merge方法很重要.
},
first: function() {
return this.eq( 0 );
*直接看eq
},
last: function() {
return this.eq( -1 );
*直接看eq
},
eq: function( i ) {
var len = this.length,
j = +i + ( i < 0 ? len : 0 );
*这里有个细节,+i ,当i是字符串'1'时 可以转换成number类型 1,相当于调用Number()?
* 当然跟parseInt 和parseFloat 还是有区别.遇到字类似'1.1a'类型会变成NaN
* 但很简洁!
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 );
}));
*map源码在676行,结构上和each 似乎差不多,不过返回dom数组.
*map和each 应该是jq隐式迭代专用函数了.
},
end: function() {*兼容,如果没有prevObject时返回自身.
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
*这三个是把数组上的方法移植过来了.
};