jQuery方法源码解析--jQuery($)方法
注:
1.本文分析的代码为jQuery.1.11.1版本,在官网上下载未压缩版即可
2.转载请注明出处
jQuery方法:
这个方法大家都不陌生,在使用过程中,它还有另外一个名字,美元符号:$,$(...)其实就是jQuery(...);
它有很多种用法,通常都返回一个jquery对象,也可以作为$(document).ready(...);的简写形式,分析之前先看一下jQuery都有什么用法。
1.jQuery( selector [, context ] )
selector:String;
context:Element or jQuery
在context中返回一个符合selecttor表达式的jquery对象,context默认为$(document),如果找不到自然返回一个空的jquery对象,也就是length为0的jquery对象;
eg:
$( "div.foo" ).click(function() {
$( "span", this ).addClass( "bar" );
});
$( "span", this ).addClass( "bar" );
});
2.jQuery( element ),jQuery( elementArray )
element :Element
elementArray : Array of Element
what is jQuery Object??
总结一下,jQuery对象,就是在js对象的基础上,包装了一些jQuery方法的对象,有一个英文词形容的比较贴切,就是wrap(包裹).
jQuery方法的结构
在jQuery1.11.1的源码中,搜索jQuery,在源码开始的不远处,就可以看到如下的代码:
jquery.init方法
将DOM元素或数组转化(包装)为一个jquery对象。PS:从实现的原理上来说,称之为包装更合适。
eg:$(document),$(this),$(document.getElementsByTagName("div"));
3.jQuery( object )
object:PlainObject
将一个普通对象转为jQuery对象,普通对象:PlainObject,就是一个仅有键值对的对象;
这有什么用处?!暂时不大了解,官网给出解释:
At present, the only operations supported on plain JavaScript objects wrapped in jQuery are:
.data(),.prop(),.on(), .off(), .trigger() and .triggerHandler().
The use of .data() (or any method requiring .data()) on a plain object will result in a new
property on the object called jQuery{randomNumber} (eg. jQuery123456789).
.data(),.prop(),.on(), .off(), .trigger() and .triggerHandler().
The use of .data() (or any method requiring .data()) on a plain object will result in a new
property on the object called jQuery{randomNumber} (eg. jQuery123456789).
4.jQuery( selection )
selection :jQuery
复制一个jQuery对象
When a jQuery object is passed to the
$()
function, a clone of the object is created.
This new jQuery object references the same DOM elements as the initial one.
5.jQuery()
返回一个空的jQuery对象
6.jQuery( html [, ownerDocument ] )
html:htmlString
ownerDocument:document
创建一个新的dom元素,第二个参数指定创建元素所使用的document;当有多个iframe场景可能会用到第二个元素
eg:$( "<p id='test'>My <em>new</em> text</p>" ).appendTo( "body" );
7.jQuery( html, attributes )
html: htmlString,该场景下必须是单标签
attributes : PlainObject,一个使用键值对的形式描述元素属性的对象
eg:
$( "<div></div>", {
"class": "my-div",
on: {
touchstart: function( event ) {
// Do something}
}}).appendTo( "body" );
$( "<div></div>" )
.addClass( "my-div" )
.on({
touchstart: function( event ) {
// Do something
}
})
.appendTo( "body" );
"class": "my-div",
on: {
touchstart: function( event ) {
// Do something}
}}).appendTo( "body" );
如果第二个对象的属性名字在jQuery中是一个函数(还可能是内部函数),会调用函数,属性值会被当做参数。如果attributes过于复杂,个人不建议这么写,写成下面这样比较清晰。
.addClass( "my-div" )
.on({
touchstart: function( event ) {
// Do something
}
})
.appendTo( "body" );
8.jQuery( callback )
callback:function
没什么好说的,就是$(document).ready(callback)的简写形式。
what is jQuery Object??
jQuery方法,通常返回的是一个jQuery对象,那么,首先我们必须要知道jQuery对象到底是什么,它大致长成什么样子.
在html写几个li:
<body>
<ol>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
</body>
<ol>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
</body>
创建jQuery对象,并输出出来:
console.log($("li"));
在chrome中运行如下:
可以看出来jQuery对象,具有类数组的结构,有着从0到length-1的索引和length属性(但没有数组的concat或者slice等其他方法),而每个li,就是element DOM对象.这个类数组的结构,可以看做是原生的js对象数组,除了这个类数组的结构外,其他的就是一些jQuery属性和方法,打开指向prototype的隐性指针,就可以看到很多我们常用的jQuery方法.
总结一下,jQuery对象,就是在js对象的基础上,包装了一些jQuery方法的对象,有一个英文词形容的比较贴切,就是wrap(包裹).
jQuery方法的结构
在jQuery1.11.1的源码中,搜索jQuery,在源码开始的不远处,就可以看到如下的代码:
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
}
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
}
可以看到,jQuery就是一个函数,返回了一个对象.
== ,what?! 居然返回的是一个 jQuery.fn.init对象?!
我们搜一下jQuery.fn.init这货:
我们搜一下jQuery.fn.init这货:
init = jQuery.fn.init = function( selector, context ) {
var match, elem;
var match, elem;
......
};
在这个函数结束之后,下面这个代码扑面而来(不同版本这些代码的位置不一定一样,还是搜索比较靠谱):
// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;
init.prototype = jQuery.fn;
再搜索"jQuery.fn =",可以看到jQuery.fn = jQuery.prototype = {...};
这回明了了,jQuery.fn就是jQuery这个函数的prototype,而init.prototype = jQuery.fn,就是说:
jQuery.fn.init.prototype = jQuery.prototype
那么通过jQuery.fn.init.prototype的这个函数构造出来的对象,就可以通过指向jQuery.fn.init.prototype的引用,找到jQuery的prototype下的方法,从某种意义上来说,通过jQuery.fn.init构造出来的方法,就是jQuery对象了.
总结一下,jQuery对象的构造其实是通过jQuery.fn.init来实现的,通过init.prototype = jQuery.prototype,令jQuery.fn.init对象可以使用jQuery.prototype的方法属性.
jquery.init方法
在分析init方法之前,必须认识该方法用到的两个正则表达式:
第一个:
// A simple way to check for HTML strings
// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
// Strict HTML recognition (#11290: must start with <)
rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/
// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
// Strict HTML recognition (#11290: must start with <)
rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/
从注释上看也知道,这是匹配html的正则表达式,因为某个bug的原因,还要同时匹配#id形式的字符串,进行优先处理.
/^(...)$/-->包裹着开始和结束定位符,匹配整个字符串;
(?:...) --> ?:表示这个分组不捕获,仅仅是起分组的作用,可以提高性能;
s*(<[wW]+>[^>]* -->匹配可以空白字符开头,可以任意非'>'结尾的<tag>形式的字符串
#([w-]*) -->匹配以#开头,跟着任意字母或减号的字符串
整体翻译过来,就是匹配<tag>或#id形式的字符串
执行一下:
rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/;
console.log(rquickExpr.exec("<tag>"));
console.log(rquickExpr.exec("<tag>"));
输出:["<tag>", "<tag>", undefined, index: 0, input: "<tag>"]
另外一个正则:
var rsingleTag = (/^<(w+)s*/?>(?:</1>|)$/);
看名字可以猜到,这是个匹配单标签的形式。
<(w+)s*/?> -->匹配一个标签,可以使<tag>或<tag />的形式
(?:</1>|) -->1表示分组1中捕获的字符串,也就是匹配和前面一样的闭合标签</tag>或者为空字符串(注意后面的或者符号什么也没跟,就是或者是空字符串)
合起来就是匹配<tag /> 或者是 <tag></tag>;
不难发现,这个正则匹配不了<tag attr="..."></tag>这种带属性的单标签,也匹配不了<tag>asdf</tag>这种带文本节点的标签,跟确切的说,这个正则只匹配单的空标签.
jQuery方法可以说是JQ最核心的方法了,而它通过init方法实现,那么它是不是很长呢?!
还真不是,也就100行,因为它还调用了jQuery的其他方法,下面单就这个init方法结构,进行分析。
直接搜索jQuery.fn.init,很快就能找到init函数的源码,大致过一下,发现很多if else,这和jQuery这个函数功能的多样性相符合;
下面直接在代码中写注释来解析这段代码
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159init = jQuery.fn.init = function( selector, context ) { var match, elem; //处理jQuery()形式,返回空的jQuery对象 // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; } // Handle HTML strings /*#源码分析 *匹配html(以<开头,以>结尾,或者是<tag>...形式,推荐写严格的html),或者是#id形式,并且确保,#id情况下,没有context */ if ( typeof selector === "string" ) { if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { /*#源码分析 *假设以<开头,以>结尾的,并且长度大于3的String都是htmlString,然后存到match中 */ // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = rquickExpr.exec( selector ); } /*#源码分析 *what is match? *我们分析过rquickExpr这个正则表达式,它有两个分组(最外面的那个分组不捕获,没算), *这样使用exec方法后,如果匹配到了,则应该返回一个长度为3,包括index和input属性的数组。 *那么match[0]是整体匹配的数组,match[1]是匹配的<tag>标签(如果有),match[2]匹配#id(如果有) *显然match[1],match[2]只能有一个被捕获 */ // Match html or make sure no context is specified for #id if ( match && (match[1] || !context) ) { //处理jQuery( html [, ownerDocument ] ),根据html生成dom元素,返回jQuery对象,就像下面的注释$(html) -> $(array) //match[1] // HANDLE: $(html) -> $(array) if ( match[1] ) { /*#源码分析 *如果context是jQuery对象,转为js原生对象. *我们说过,jQuery对象就是给js原生对象包装了了一些方法的对象,而原生对象以类数组的形式存在jQuery中, *所以context[0],就是去jQuery对象中的第一个原生对象,在这里context期待传入的就是document或者$(document) */ context = context instanceof jQuery ? context[0] : context; /*#源码分析 *这里调用了jQuery.parseHTML方法,就是把htmlString转为dom数组 *还调用了jQuery.merge(first,second),接收两个"类数组"参数, *这个方法是把第二个数组追加到第一个数组尾部,会改变第一个数组 *前面讲过,jQuery对象具有类数组结构,当前还没有操作this,它的length = 0 *所以下面这段代码,就是把htmlString转为dom数组并追加到this的尾部。 */ // scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge( this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true ) ); /*#源码分析 *处理jQuery( html, attributes )这种用法 *第一个参数必须是单标签,第二个参数是一个普通对象,类似{html:"hello world",id:"test"} *注意,这种情况下,会走上面的分支,已经把单标签转为dom并拼接到了this中了 */ // HANDLE: $(html, props) if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { /*#源码分析 *这里的match是context的属性 *如果有match的方法,就会调用match方法 *比如{html:"hello world",id:"test"},就会调用this.html("hello world")方法 *否则按照属性处理 */ // Properties of context are called as methods if possible if ( jQuery.isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); // ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); } } } return this; /*#源码分析 *处理match[2]被捕获到的情况,也就是#id的情况 */ // 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 // 为了兼容Blackberry 4.6中的一个bug,不但判断element,还要判断elem.parentNode if ( elem && elem.parentNode ) { // Handle the case where IE and Opera return items // by name instead of ID /*#源码分析 *早期的IE的document.getElementById()有bug,在查找元素时,会把表单的元素的name也看成是id, *这时候使用find方法来兼容,搜索一下"rootjQuery =",会发现rootjQuery = jQuery( document ); */ if ( elem.id !== match[2] ) { return rootjQuery.find( selector ); } /*#源码分析 *否则直接放到this[0]中 */ // Otherwise, we inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; } /*#源码分析 *处理$(复杂选择器的情况),采用find方法处理并返回, *这里两个else的判断,就是为了确保是jQuery对象调用find方法, *如果context不是jQuery对象,使用constructor构造一个 *context.jquery-->jquery是jQuery.fn上的一个版本属性,在此用这个来判断是否是jQuery对象。 */ // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return ( context || rootjQuery ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } /*#源码分析 *else selector不是String类型的 */ // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { /*#源码分析 *处理$(DOMElement)形式,这个比较简单,就是加到this[0]上 */ this.context = this[0] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { /*#源码分析 *$(function)形式,就是调用了$(document).ready()方法 */ return typeof rootjQuery.ready !== "undefined" ? rootjQuery.ready( selector ) : // Execute immediately if ready is not present selector( jQuery ); } /*#源码分析 *如果selector.selector !== undefined,那么selector本身可能就是一个jQuery对象,做了如下处理,为什么呢?!不知道 */ if ( selector.selector !== undefined ) { this.selector = selector.selector; this.context = selector.context; } /*#源码分析 *jQuery.makeArray(arr)是把类数组,转为纯数组形式,API是这么说的. *实际上makeArray还有第二个参数,但仅限内部使用,就是现在这种情况 *jQuery.makeArray( arr, results ) -->把调用jQuery.merge把arr数组追加到results尾部 *这里会处理jQuery( elementArray )、jQuery( object )这两种情况 */ return jQuery.makeArray( selector, this ); };
下集预告
经过分析之后大概对jQuery这个函数有了大致的了解,这个函数还调用了其他的jQuery的工具函数:
比如jQuery.merge、 jQuery.parseHTML、jQuery.isPlainObject、find、$(document).ready()、jQuery.makeArray方法
下一篇文章对以上的方法中选几个简单的进行分析,太复杂的,先放一放吧。
最后
以上就是陶醉冬瓜最近收集整理的关于jQuery方法源码解析--jQuery($)方法(一)的全部内容,更多相关jQuery方法源码解析--jQuery($)方法(一)内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复