<b>2.7 原型屬性和方法</b>
在構造jquery對象子產品時,除了2.3節和2.6節已經介紹和分析的jquery.fn.init()和jquery.fn.extend()外,還定義了一些其他的原型屬性和方法,其整體源碼結構如代碼清單2-2所示。
代碼清單2-2 原型屬性和方法
97 jquery.fn =
jquery.prototype = {
98
constructor: jquery,
99
init: function( selector, context, rootjquery ) {}
210
selector: "",
213
jquery: "1.7.1",
216
length: 0,
219
size: function() {},
223
toarray: function() {},
229
get: function( num ) {},
241
pushstack: function( elems, name, selector ) {},
270
each: function( callback, args ) {},
274
ready: function( fn ) {}, //
284
eq: function( i ) {},
291
first: function() {},
295
last: function() {},
299
slice: function() {},
304
map: function( callback ) {},
310
end: function() {},
316
push: push,
317
sort: [].sort,
318
splice: [].splice
319 };
其中.ready()用于綁定ready事件,将在9.11節進行介紹和分析。下面對其他的原型屬性和方法逐一介紹和分析。
2.7.1 .selector、.jquery、.length、.size()
屬性selector用于記錄jquery查找和過濾dom元素時的選擇器表達式,但不一定是可執行的選擇器表達式,該屬性更多的是為了友善調試。下面是一些示例:
$('div').find('p').selector // "div p"
$('div p').selector // "div p"
$('div').first().selector //
"div.slice(0,1)"
屬性jquery表示正在使用的jquery版本号。
屬性.length表示目前jquery對象中元素的個數。
方法.size()傳回目前jquery對象中元素的個數。方法.size()在功能上等價于屬性.length,但應該優先使用屬性.length,因為它沒有函數調用開銷。
屬性.selector、.jquery、.length、.size()的相關代碼如下所示:
209
// start with an empty selector
211
212
// the current version of jquery being used
214
215
// the default length of a jquery object is 0
217
218
// the number of elements contained in the matched element set
size: function() {
220
return this.length;
221
},
222
2.7.2 .toarray()、.get(
[index] )
1.?.toarray()
方法.toarray()将目前jquery對象轉換為真正的數組,轉換後的數組包含了所有元素。方法.toarray()的實作巧妙地借用了數組的方法slice(),相關的代碼如下所示:
86
// save a reference to some core methods
87
tostring = object.prototype.tostring,
88
hasown = object.prototype.hasownproperty,
89
push = array.prototype.push,
90
slice = array.prototype.slice,
91
trim = string.prototype.trim,
92
indexof = array.prototype.indexof,
223
toarray: function() {
224
return slice.call( this, 0 );
225
第86~92行:連同slice()一起聲明的還有tostring()、hasown()、trim()、indexof(),這裡通過聲明對這些核心方法的引用,使得在jquery代碼中可以借用這些核心方法的功能,執行時可通過方法call()和apply()指定方法執行的環境,即關鍵字this所引用的對象。這種“借雞下蛋”的技巧非常值得借鑒。
數組方法slice()傳回數組的一部分,文法如下:
array.slice(start, end)
// 參數start 表示數組片段開始處的下标。如果是負數,它聲明從數組末尾開始算起的位置
// 參數end 表示數組片段結束處的後一個元素的下标。如果沒有指定這個參數,切分的數組包含從 start 開始到數組結束的所有元素。如果這個參數是負數,它聲明的是從數組尾部開始算起的元素
2.?.get( [index] )
方法.get( [index] )傳回目前jquery對象中指定位置的元素或包含了全部元素的數組。如果沒有傳入參數,則調用.toarray()傳回包含了所有元素的數組;如果指定了參數index,則傳回一個單獨的元素;參數index從0開始計算,并且支援負數,負數表示從元素集合末尾開始計算。相關代碼如下所示:
227
// get the nth element in the matched element set or
228
// get the whole matched element set as a clean array
get: function( num ) {
230
return num == null ?
231
232 // return a 'clean' array
233
this.toarray() :
234
235 // return just the object
236 ( num < 0 ? this[ this.length +
num ] : this[ num ] );
237
第236行:先判斷num是否小于0,如果小于0,則用length + num重新計算下标,然後使用數組通路操作符([])擷取指定位置的元素,這是支援下标為負數的一個小技巧;如果num大于等于0,則直接傳回指定位置的元素。
<b>2.7.3 .each(</b>
function(index, element) )、jquery.each( collection, callback (indexinarray, valueofelement) )
1.?.each( function(index, element) )
方法.each()周遊目前jquery對象,并在每個元素上執行回調函數。每當回調函數執行時,會傳遞目前循環次數作為參數,循環次數從0開始計數;更重要的是,回調函數是在目前元素為上下文的語境中觸發的,即關鍵字this總是指向目前元素;在回調函數中傳回false可以終止周遊。
方法.each()内部通過簡單的調用靜态方法jquery.each()實作,相關代碼如下所示:
267
// execute a callback for every element in the matched set.
268
// (you can seed the arguments with an array of args, but this is
269
// only used internally.)
each: function( callback, args ) {
271
return jquery.each( this, callback, args );
272
2.?jquery.each( collection, callback(indexinarray,
valueofelement) )
靜态方法jquery.each()是一個通用的周遊疊代方法,用于無縫地周遊對象和數組。對于數組和含有length屬性的類數組對象(如函數參數對象arguments),該方法通過下标周遊,從0到length-1;對于其他對象則通過屬性名周遊(for-in)。在周遊過程中,如果回調函數傳回false,則結束周遊。相關代碼如下所示:
627
// args is for internal usage only
628
each: function( object, callback, args ) {
629
var name, i = 0,
630 length = object.length,
631 isobj = length === undefined ||
jquery.isfunction( object );
632
633
if ( args ) {
634 if ( isobj ) {
635 for ( name in object ) {
636 if ( callback.apply(
object[ name ], args ) === false ) {
637 break;
638 }
639 }
640 } else {
641 for ( ; i < length; ) {
642 if ( callback.apply(
object[ i++ ], args ) === false ) {
643 break;
644 }
645 }
646 }
647
648
// a special, fast, case for the most common use of each
649
} else {
650 if ( isobj ) {
651 for ( name in object ) {
652 if ( callback.call(
object[ name ], name, object[ name ] ) === false ) {
653 break;
654 }
655 }
656 } else {
657 for ( ; i < length; ) {
658 if ( callback.call(
object[ i ], i, object[ i++ ] ) === false ) {
659 break;
660 }
661 }
662 }
663
}
664
665
return object;
666
第628行:定義方法jquery.each( object, callback, args ),它接受3個參數。
參數object:待周遊的對象或數組。
參數callback:回調函數,會在數組的每個元素或對象的每個屬性上執行。
參數args:傳給回調函數callback的參數數組,可選。如果沒有傳入參數args,則執行回調函數時會傳入兩個參數(下标或屬性名,對應的元素或屬性值)如果傳入了參數args,則隻把該參數傳給回調函數。
第631行:變量isobj表示參數object是對象還是數組,以便決定周遊方式。如果object.length是undefined或object是函數,則認為object是對象,設定變量isobj為true,将通過屬性名周遊;否則認為是數組或類數組對象,設定變量isobj為false,将通過下标周遊。
第633~646行:如果傳入了參數args,對于對象,通過for-in循環周遊屬性名,對于數組或類數組對象,則通過for循環周遊下标,執行回調函數時隻傳入一個參數args。
注意,執行回調函數callback時通過方法apply()指定this關鍵字所引用的對象,同時要求并假設參數args是數組,如果不是就會抛出typeerror。
第648~663行:如果未傳入參數args,對于對象,通過for-in循環周遊屬性名,對于數組或類數組對象,則通過for循環周遊下标,執行回調函數時傳入兩個參數:下标或屬性名,對應的元素或屬性值。
注意,執行回調函數callback時通過方法call()指定this關鍵字所引用的對象。
似乎jquery.each()的代碼略顯啰嗦,因為第633~646行和第648~663行的代碼相似度很高,隻是觸發回調函數的方式和參數不同,完全可以考慮把它們合并,在合并後的代碼中根據變量isobj的值決定觸發方式和參數,這樣就可以減少一半的代碼,但是,這也會導緻在周遊過程中需要反複判斷變量isobj的值。兩權相較,方法jquery.each()選擇了“略顯啰嗦的”代碼來避免性能下降。
第665行:最後,傳回傳入的參數object。方法.each()調用jquery.each()時,把目前jquery對象作為參數object傳入,這裡傳回該參數,以支援鍊式文法。
<b>2.7.4 .map(</b>
callback(index, domelement) )、jquery.map( arrayorobject, callback(value, indexorkey) )
1.?.map( callback(index, domelement) )
方法.map()周遊目前jquery對象,在每個元素上執行回調函數,并将回調函數的傳回值放入一個新jquery對象中。該方法常用于擷取或設定dom元素集合的值。
執行回調函數時,關鍵字this指向目前元素。回調函數可以傳回一個獨立的資料項或資料項數組,傳回值将被插入結果集中;如果傳回一個數組,數組中的元素會被插入結果集;如果回調函數傳回null或undefined,則不會插入任何元素。
方法.map()内部通過靜态方法jquery.map()和原型方法.pushstack()實作,相關代碼如下所示:
map: function( callback ) {
305
return this.pushstack( jquery.map(this, function( elem, i ) {
306 return callback.call( elem, i,
elem );
307
}));
308
原型方法.pushstack()将在2.7.5節介紹和分析。
2.?jquery.map( arrayorobject,
callback(value, indexorkey) )
靜态方法jquery.map()對數組中的每個元素或對象的每個屬性調用一個回調函數,并将回調函數的傳回值放入一個新的數組中。執行回調函數時傳入兩個參數:數組元素或屬性值,元素下标或屬性名。關鍵字this指向全局對象window。回調函數的傳回值會被放入新的數組中;如果傳回一個數組,數組中将被扁平化後插入結果集;如果傳回null或undefined,則不會放入任何元素。相關代碼如下所示:
760
// arg is for internal usage only
761
map: function( elems, callback, arg ) {
762
var value, key, ret = [],
763 i = 0,
764 length = elems.length,
765 // jquery objects are treated as
arrays
766 isarray = elems instanceof jquery
|| length !== undefined && typeof length === "number"
&& ( ( length > 0 && elems[ 0 ] && elems[ length -1
] ) || length === 0 || jquery.isarray( elems ) ) ;
767
768
// go through the array, translating each of the items to their
769
if ( isarray ) {
770 for ( ; i < length; i++ ) {
771 value = callback( elems[ i ],
i, arg );
772
773 if ( value != null ) {
774 ret[ ret.length ] = value;
775 }
776 }
777
778
// go through every key on the object,
779
780 for ( key in elems ) {
781 value = callback( elems[ key
], key, arg );
782
783 if ( value != null ) {
784 ret[ ret.length ] = value;
785 }
786 }
787
788
789
// fl atten any nested arrays
790
return ret.concat.apply( [], ret );
791
第761行:定義方法jquery.map( elems, callback, arg ),它接受3個參數:
參數elems:待周遊的數組或對象。
參數callback:回調函數,會在數組的每個元素或對象的每個屬性上執行。執行時傳入兩個參數:數組元素或屬性值,元素下标或屬性名。
參數arg:僅限于jquery内部使用。如果調用jquery.map()時傳入了參數arg,則該參數會被傳給回調函數callback。
第766行:變量isarray表示參數elems是否是數組,以便決定周遊方式。如果為true,将通過下标周遊;否則将通過屬性名周遊。這行複合布爾表達式有些長,為了友善分析,将這行代碼等價地格式化為下面的形式:
isarray = elems instanceof jquery
||
length !== undefined && typeof length === "number"
&& ( ( length > 0 && elems[ 0 ] && elems[
length -1 ] )
|| length === 0
|| jquery.isarray( elems ) ) ;
如果elems是jquery對象,則變量isarray為true;如果elem.length是數值型,且滿足以下條件之一,則變量isarray為true:
length大于0,且elems[0]存在,且elems[
length -1 ]存在,即elems是一個類數組對象。
length等于0。
elems是真正的數組。
第769~776行:對于數組或類數組對象,則通過for循環周遊下标,為每個元素執行回調函數callback,執行時依次傳入三個參數:元素、下标、arg。如果回調函數的傳回值不是null和undefined,則把傳回值放入結果集ret。
第778~787行:對于對象,則通過for-in循環周遊屬性名,為每個屬性值執行回調函數callback,執行時依次傳入三個參數:屬性值、屬性名、arg。如果回調函數的傳回值不是null和undefined,則把傳回值放入結果集ret。
第790行:最後在空數組[]上調用方法concat()扁平化結果集ret中的元素,并傳回。
<b>2.7.5 .pushstack(</b>
elements, name, arguments )
原型方法.pushstack()建立一個新的空jquery對象,然後把dom元素集合放入這個jquery對象中,并保留對目前jquery對象的引用。
原型方法.pushstack()是核心方法之一,它為以下方法提供支援:
jquery對象周遊:.eq()、.first()、.last()、.slice()、.map()。
dom查找、過濾:.find()、.not()、.filter()、.closest()、.add()、.andself()。
dom周遊:.parent()、.parents()、.parentsuntil()、.next()、.prev()、.nextall()、.prevall()、
.nextunit()、.prevunit()、.siblings()、.children()、.contents()。
dom插入:jquery.before()、jquery.after()、jquery.replacewith()、.append()、.prepent()、
.before()、.after()、.replacewith()。
相關代碼如下所示:
239
// take an array of elements and push it onto the stack
240
// (returning the new matched element set)
pushstack: function( elems, name, selector ) {
242
// build a new jquery matched element set
243
var ret = this.constructor();
244
245
if ( jquery.isarray( elems ) ) {
246 push.apply( ret, elems );
247
248
249 jquery.merge( ret, elems );
250
251
252
// add the old object onto the stack (as a reference)
253
ret.prevobject = this;
254
255
ret.context = this.context;
256
257
if ( name ===
"find" ) {
258 ret.selector = this.selector + (
this.selector ? " " : "" ) + selector;
259
} else if ( name ) {
260 ret.selector = this.selector +
"." + name + "(" + selector + ")";
261
262
263
// return the newly-formed element set
264
return ret;
265
第241行:定義方法.push( elems, name, selector ),它接受3個參數:
參數elems:将放入新jquery對象的元素數組(或類數組對象)。
參數name:産生元素數組elems的jquery方法名。
參數selector:傳給jquery方法的參數,用于修正原型屬性.selector。
第243行:構造一個新的空jquery對象ret,this.constructor指向構造函數jquery()。
第245~250行:把參數elems合并到新jquery對象ret中。如果參數elems是數組,則借用數組方法push()插入,否則調用方法jquery.merge( first, second )合并。
第253行:在新query對象ret上設定屬性prevobject,指向目前jquery對象,進而形成一個鍊式棧。是以,方法.pushstack()的行為還可以了解為,建構一個新的jquery對象并入棧,新對象位于棧頂,這也是該方法如此命名的原因所在。
第255行:在新jquery對象ret上設定屬性prevobject,指向目前jquery對象的上下文,後續的jquery方法可能會用到這個屬性。
第257~261行:在新jquery對象ret上設定屬性selector,該屬性不一定是合法的選擇器表達式,更多的是為了友善調試,例如,下面的代碼列印了調用方法.pushstack()之後屬性selector的值:
console.log( $('div').eq(0).selector );
// div.slice(0,1)
console.log( $('div').first().selector );
console.log( $('div').last().selector );
// div.slice(-1)
console.log( $('div').slice(0,9).selector
);
// div.slice(0,9)
console.log( $('div').find('p').selector );
// div p
console.log( $('div').not('.cls').selector
// div.not(.cls)
console.log(
$('div').find('p').not('.cls').selector );
// div p.not(.cls)
第264行:最後傳回新jquery對象ret。
<b>2.7.6 .end()</b>
方法.end()結束目前鍊條中最近的篩選操作,并将比對元素集合還原為之前的狀态。相關代碼如下所示:
end: function() {
311
return this.prevobject || this.constructor(null);
312
第311行:傳回前一個jquery對象。如果屬性prevobject不存在,則建構一個空的jquery對象傳回。
方法.pushstack()用于入棧,方法.end()則用于出棧。這兩個方法可以像下面的例子這樣使用:
$('ul.first').find('.foo')
.css('background-color', 'red')
.end().find('.bar')
.css('background-color', 'green')
.end();
<b>2.7.7 .eq( index )、.first()、.last()、.slice(</b>
start [, end] )
方法.eq( index )将比對元素集合縮減為集合中指定位置的元素;方法.first()将比對元素集合縮減為集合中的第一個元素;方法.last()将比對元素集合縮減為集合中的最後一個元素;方法.slice( start [, end] )将比對元素集合縮減為指定範圍的子集。
方法.first()和.last()通過調用.eq( index )實作,.eq( index )則通過.slice( start [, end] )實作,.slice( start [,
end] )則通過調用.pushstack( elements, name, arguments )實作,方法調用鍊為.first/last()→.eq( index )→.slice(
start [, end] )→.pushstack( elements, name, arguments )。相關代碼如下所示:
284
eq: function( i ) {
285
i = +i;
286
return i === -1 ?
287 this.slice( i ) :
288 this.slice( i, i + 1 );
289
290
291
first: function() {
292
return this.eq( 0 );
293
294
295
last: function() {
296
return this.eq( -1 );
297
298
299
slice: function() {
300
return this.pushstack( slice.apply( this, arguments ),
301 "slice",
slice.call(arguments).join(",") );
302
第285行:如果參數i是字元串,則通過在前面加上一個加号把該參數轉換為數值。
第300~301行:先借用數組方法slice()從目前jquery對象中擷取指定範圍的子集(數組),再調用方法.pushstack()把子集轉換為jquery對象,同時通過屬性prevobject保留了對目前jquery對象的引用。
<b>2.7.8 .push( value, ... )、.sort(</b>
[orderfunc] )、.splice( start,deletecount, value, ... )
方法.push( value, ... )向目前jquery對象的末尾添加新元素,并傳回新長度,例如:
var foo = $(document);
foo.push( document.body ); // 2
方法.sort( [orderfunc] )對目前jquery對象中的元素進行排序,可以傳入一個比較函數來指定排序方式,例如。
var foo = $([33, 4, 1111, 222]);
foo.sort(); // [1111, 222, 33, 4]
foo.sort(function(a, b){
console.log( 'orderfun', a, b );
return a - b;
}) // [4, 33, 222, 1111]
方法.splice( start,deletecount, value, ... )向目前jquery對象中插入、删除或替換元素。如果從目前jquery對象中删除了元素,則傳回含有被删除元素的數組。例如:
var foo = $('<div id="d1"
/><div id="d2" /><div id="d3" />');
// [<div
id="d1"></div>, <div id="d2"></div>,
<div id="d3"></div>]
foo.splice( 1, 2 );
id="d2"></div>, <div id="d3"></div>]
方法.push()、.sort()、.splice()僅在内部使用,都指向同名的數組方法,是以它們的參數、功能和傳回值與數組方法完全一緻。相關代碼如下所示:
314
// for internal use only.
315
// behaves like an array's method, not like a jquery method.
<b>2.7.9 小結</b>
構造jquery對象子產品的原型屬性和方法可以總結為圖2-8。