jQuery原始碼閱讀(十一)---each、map、grep、merge、makeArray、inArray解讀
這兩天都在看jQuery原始碼中的靜態方法,上一篇部落格介紹了型別檢測這一類靜態方法,這次主要對後面幾個常用的方法進行分析。主要包括:
each: function(){}
map: function(){}
grep: function()
merge: function(){}
makeArray: function(){}
inArray: function(){}
each、map、grep
each、map、grep這三個函式是比較相似的,所以想放在一起來分析。
首先先要了解三個函式的功能:
var arr = [2,3,5,9];
var res = $.each(arr, function(i, item){
console.log(i + ' ' + item);
})
var obj = [3,5,7,9,2, 4, 6, 8];
res = $.map(obj, function( item, i){
if(item % 2 == 0)
{
return item;
}
});
var arr = [4,2,8,6,4,3,0,6];
var res = $.grep(arr, function(item, i){
return item > 4; //過濾條件,返回一個陣列
});
var res1 = $.grep(arr, function(item, i){
return item > 4; //過濾條件,返回一個陣列
}, true);
上面是三種方法的功能測試,可以看到,each主要是對陣列(物件)中的每個元素進行遍歷或者一系列操作;map和grep的話其實都可以認為是過濾函式,按照一定的規則去過濾和對映,不同的是,map可以對 物件來操作,但是grep只能過濾陣列。
下來分析一下原始碼。
each原始碼:
function( object, callback, args ) {
var name, i = 0,
length = object.length, //如果是物件,沒有length屬性
//判斷是否為物件或者函式,函式的話有length屬性,所以需要另外判斷
isObj = length === undefined || jQuery.isFunction( object );
//args是要傳入到callback裡面的引數,注意這個args需要傳陣列的形式,因為下面利用的是callback.apply。
if ( args ) {
//整體思路是分別對物件和陣列(或偽陣列)處理;
//object為物件時,用for ... in 來遍歷
if ( isObj ) {
for ( name in object ) {
if ( callback.apply( object[ name ], args ) === false ) {
break;
}
}
} else {
//object為陣列(偽陣列)時,直接利用for迴圈。
//對每一個元素分別調callback方法
for ( ; i < length; ) {
if ( callback.apply( object[ i++ ], args ) === false ) {
break;
}
}
}
} else {
//無引數情況與上面情況類似,預設的引數為元素索引index和陣列元素item
if ( isObj ) {
for ( name in object ) {
if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
break;
}
}
} else {
for ( ; i < length; ) {
if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
break;
}
}
}
}
return object;
}
grep原始碼:
function( elems, callback, inv ) {
//ret空陣列是用來儲存過濾元素的,最後作為結果返回
var ret = [], retVal;
inv = !!inv; //inv表示是否反轉結果;!!inv是用來將inv轉換成Boolean型別的。預設不傳改引數,就是false
for ( var i = 0, length = elems.length; i < length; i++ ) {
//對陣列中每個元素執行過濾規則(即callback函式),並將返回的結果轉換為Boolean值
retVal = !!callback( elems[ i ], i );
//若有返回值,retVal為true; 預設情況下inv為false,這時將當前元素Push進ret陣列中,儲存起來。
//同理,若inv設為true,表示將未滿足過濾規則的元素push進結果陣列裡。
if ( inv !== retVal ) {
ret.push( elems[ i ] );
}
}
return ret;
}
map原始碼:
function( elems, callback, arg ) {
var value, key, ret = [],
i = 0,
length = elems.length,
//判斷elems是否為陣列(或偽陣列)
//jQuery物件算是偽陣列
isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
// 當elems為陣列時,for迴圈遍歷每個元素,並對每個元素執行callback函式,返回結果加到ret陣列裡
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret[ ret.length ] = value;
}
}
} else {
//當elems為物件時,利用for...in 遍歷每個元素,並對每個元素調callback函式
for ( key in elems ) {
value = callback( elems[ key ], key, arg );
if ( value != null ) {
ret[ ret.length ] = value;
}
}
}
//最後是對結果的扁平化
return ret.concat.apply( [], ret );
}
這裡主要對map函式最後 扁平化進行分析。這是為了防止陣列巢狀的情況。假設這麼一種情況:
var obj = [ [1,2,3], [4,5,6]];
res = $.map(obj, function( item, i){
return item;
});
可以看到最後得到的結果陣列裡,中間並沒有嵌入陣列。這是如何解決的呢?
在jQuery原始碼中,利用了apply和陣列的原生方法concat。
ret.concat.apply( [], ret );
concat作用是將陣列進行連線,並返回一個新的陣列。利用apply,首先將空陣列和ret陣列中第一個元素進行連線,返回一個新的陣列,然後再將新的陣列和ret中第二個元素連線,以此類推。最終得到一個新陣列。
makeArray、inArray、merge
這三個方法都相當於是陣列(類陣列)上的操作。
makeArray:將傳入的引數轉換成陣列
inArray: 判斷某個元素是否在陣列中
merge: 將兩個陣列合併成陣列
makeArray原始碼:
function( array, results ) {
var ret = results || [];
if ( array != null ) {
var type = jQuery.type( array );
//array為物件,字串,函式,正則,Window物件,都認為是一個整體,直接將其加入到ret陣列中
if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
push.call( ret, array );
} else {
//array為陣列或偽陣列,呼叫jQuery.merge方法,這在[之前的部落格中](http://blog.csdn.net/u010046318/article/details/74223908)已講過
jQuery.merge( ret, array );
}
}
return ret;
}
inArray原始碼:
function( elem, array, i ) {
var len;
//不對物件和字串起作用
if ( array ) {
//調原生的Array.prototype.indexOf方法
if ( indexOf ) {
return indexOf.call( array, elem, i );
}
//若瀏覽器不支援上面方法,下面手動實現,遍歷一遍,找到陣列中元素與elem相等的元素,並返回index。沒找到,就返回-1.
len = array.length;
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
for ( ; i < len; i++ ) {
if ( i in array && array[ i ] === elem ) {
return i;
}
}
}
return -1;
}
到這裡,jQuery靜態方法基本上已經整理完了,其他的幾個方法不是很常用的,所以大致過了一遍。下來的部分應該就是jQuery各個模組的實現了,比如回撥物件,延遲物件,佇列,非同步佇列,Sizzle等等,這些底層的一些東西也用到了上面提到的靜態方法。所以後面看的過程中,一方面學習新的思想和技巧,另一方面回顧之前總結的方法。
相關文章
- jquery篩選陣列之grep、each、inArray、map的用法及遍歷json物件 [轉]jQuery陣列JSON物件
- 原始碼閱讀:SDWebImage(十一)——SDImageCache原始碼Web
- jQuery原始碼閱讀1—jQuery架構jQuery原始碼架構
- 【原始碼閱讀】AndPermission原始碼閱讀原始碼
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼
- Go 1.9 sync Map 原始碼閱讀筆記Go原始碼筆記
- jQuery原始碼閱讀(十)---jQuery靜態方法分析jQuery原始碼
- ThinkPHP6 原始碼閱讀(十一):FacadePHP原始碼
- jQuery原始碼閱讀(十三)---jQuery非同步佇列模組jQuery原始碼非同步佇列
- jQuery原始碼閱讀(十二)---Callbacks回撥物件jQuery原始碼物件
- 閱讀 jQuery 原始碼的18個驚喜jQuery原始碼
- 【原始碼閱讀】Glide原始碼閱讀之with方法(一)原始碼IDE
- 【原始碼閱讀】Glide原始碼閱讀之into方法(三)原始碼IDE
- PostgreSQL FSM(Free Space Map) 原始碼解讀SQL原始碼
- jQuery map和each用法jQuery
- jQuery原始碼閱讀(九)---ready函式理解jQuery原始碼函式
- ReactorKit原始碼閱讀React原始碼
- AQS原始碼閱讀AQS原始碼
- CountDownLatch原始碼閱讀CountDownLatch原始碼
- HashMap 原始碼閱讀HashMap原始碼
- delta原始碼閱讀原始碼
- 原始碼閱讀-HashMap原始碼HashMap
- NGINX原始碼閱讀Nginx原始碼
- Mux 原始碼閱讀UX原始碼
- HashMap原始碼閱讀HashMap原始碼
- fuzz原始碼閱讀原始碼
- RunLoop 原始碼閱讀OOP原始碼
- express 原始碼閱讀Express原始碼
- muduo原始碼閱讀原始碼
- stack原始碼閱讀原始碼
- jQuery的extend方法原始碼解讀jQuery原始碼
- 【原始碼閱讀】Glide原始碼閱讀之load方法(二)原始碼IDE
- JDK原始碼閱讀:Object類閱讀筆記JDK原始碼Object筆記
- 原始碼閱讀:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking原始碼UIIndicatorView
- 【詳解】ThreadPoolExecutor原始碼閱讀(三)thread原始碼
- 【詳解】ThreadPoolExecutor原始碼閱讀(二)thread原始碼
- 【詳解】ThreadPoolExecutor原始碼閱讀(一)thread原始碼
- JDK原始碼閱讀(5):HashTable類閱讀筆記JDK原始碼筆記