jq 96-280 方法属性(原型链上,实例方法属性)

"Hello World, Hello Blog"

Posted by wudimingwo on December 15, 2018
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
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 ) {*用来匹配'<someString>'的情况
				// 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-]*))$/,
                          *匹配的情况:$('<div></div>'),$(#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>|)$/,
*匹配的情况 $('<div/>')?
*jQuery.isPlainObject() 判断是否为对象,但不能是node元素,也不能是window
*所以此处的意思是,context如果不是dom节点?
*匹配的情况 $('<div>',{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 ] );
*匹配的情况 $('<div>',{html:'<div>123</div>'});这你就很牛逼了.
*这个我服,尽管我可能永远都不会这么用,但开启各种接口,我是真的服!
*在一个原型上定义了方法,我传一个对象,对象名用来调用原型上的方法
*对象值用来当做参数传给该方法.
							// ...and otherwise set as attributes
							} else {
								this.attr( match, context[ match ] );
                        *匹配的情况 $('<div>',{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
*这三个是把数组上的方法移植过来了.
};