JavaScript專題之jQuery通用遍歷方法each的實現

冴羽發表於2017-08-03

JavaScript 專題系列第十一篇,講解 jQuery 通用遍歷方法 each 的實現

each介紹

jQuery 的 each 方法,作為一個通用遍歷方法,可用於遍歷物件和陣列。

語法為:

jQuery.each(object, [callback])複製程式碼

回撥函式擁有兩個引數:第一個為物件的成員或陣列的索引,第二個為對應變數或內容。

// 遍歷陣列$.each( [0,1,2], function(i, n){ 
console.log( "Item #" + i + ": " + n );

});
// Item #0: 0// Item #1: 1// Item #2: 2複製程式碼
// 遍歷物件$.each({ 
name: "John", lang: "JS"
}, function(i, n) {
console.log("Name: " + i + ", Value: " + n);

});
// Name: name, Value: John// Name: lang, Value: JS複製程式碼

退出迴圈

儘管 ES5 提供了 forEach 方法,但是 forEach 沒有辦法中止或者跳出 forEach 迴圈,除了丟擲一個異常。但是對於 jQuery 的 each 函式,如果需要退出 each 迴圈可使回撥函式返回 false,其它返回值將被忽略。

$.each( [0, 1, 2, 3, 4, 5], function(i, n){ 
if (i >
2) return false;
console.log( "Item #" + i + ": " + n );

});
// Item #0: 0// Item #1: 1// Item #2: 2複製程式碼

第一版

那麼我們該怎麼實現這樣一個 each 方法呢?

首先,我們肯定要根據引數的型別進行判斷,如果是陣列,就呼叫 for 迴圈,如果是物件,就使用 for in 迴圈,有一個例外是類陣列物件,對於類陣列物件,我們依然可以使用 for 迴圈。

更多關於類陣列物件的知識,我們可以檢視《JavaScript專題之類陣列物件與arguments》

那麼又該如何判斷類陣列物件和陣列呢?實際上,我們在《JavaScript專題之型別判斷(下)》就講過jQuery 陣列和類陣列物件判斷函式 isArrayLike 的實現。

所以,我們可以輕鬆寫出第一版:

// 第一版function each(obj, callback) { 
var length, i = 0;
if ( isArrayLike(obj) ) {
length = obj.length;
for ( ;
i <
length;
i++ ) {
callback(i, obj[i])
}
} else {
for ( i in obj ) {
callback(i, obj[i])
}
} return obj;

}複製程式碼

中止迴圈

現在已經可以遍歷物件和陣列了,但是依然有一個效果沒有實現,就是中止迴圈,按照 jQuery each 的實現,當回撥函式返回 false 的時候,我們就中止迴圈。這個實現起來也很簡單:

我們只用把:

callback(i, obj[i])複製程式碼

替換成:

if (callback(i, obj[i]) === false) { 
break;

}複製程式碼

輕鬆實現中止迴圈的功能。

this

我們在實際的開發中,我們有時會在 callback 函式中用到 this,先舉個不怎麼恰當的例子:

// 我們給每個人新增一個 age 屬性,age 的值為 18 + indexvar person = [    {name: 'kevin'
}, {name: 'daisy'
}]$.each(person, function(index, item){
this.age = 18 + index;

})console.log(person)複製程式碼

這個時候,我們就希望 this 能指向當前遍歷的元素,然後給每個元素新增 age 屬性。

指定 this,我們可以使用 call 或者 apply,其實也很簡單:

我們把:

if (callback(i, obj[i]) === false) { 
break;

}複製程式碼

替換成:

if (callback.call(obj[i], i, obj[i]) === false) { 
break;

}複製程式碼

關於 this,我們再舉個常用的例子:

$.each($("p"), function(){ 
$(this).hover(function(){
...
});

})複製程式碼

雖然我們經常會這樣寫:

$("p").each(function(){ 
$(this).hover(function(){
...
});

})複製程式碼

但是因為 $("
p"
).each() 方法是定義在 jQuery 函式的 prototype 物件上面的,而 $.data()方法是定義 jQuery 函式上面的,呼叫的時候不從複雜的 jQuery 物件上呼叫,速度快得多。所以我們推薦使用第一種寫法。

回到第一種寫法上,就是因為將 this 指向了當前 DOM 元素,我們才能使用 $(this)將當前 DOM 元素包裝成 jQuery 物件,優雅的使用 hover 方法。

所以最終的 each 原始碼為:

function each(obj, callback) { 
var length, i = 0;
if (isArrayLike(obj)) {
length = obj.length;
for (;
i <
length;
i++) {
if (callback.call(obj[i], i, obj[i]) === false) {
break;

}
}
} else {
for (i in obj) {
if (callback.call(obj[i], i, obj[i]) === false) {
break;

}
}
} return obj;

}複製程式碼

效能比較

我們在效能上比較下 for 迴圈和 each 函式:

var arr = Array.from({length: 1000000
}, (v, i) =>
i);
console.time('for')var i = 0;
for (;
i <
arr.length;
i++) {
i += arr[i];

}console.timeEnd('for')console.time('each')var j = 0;
$.each(arr, function(index, item){
j += item;

})console.timeEnd('each')複製程式碼

這裡顯示一次運算的結果:

效能比較
效能比較

從上圖可以看出,for 迴圈的效能是明顯好於 each 函式的,each 函式本質上也是用的 for 迴圈,到底是慢在了哪裡呢?

我們再看一個例子:

function each(obj, callback) { 
var i = 0;
var length = obj.length for (;
i <
length;
i++) {
value = callback(i, obj[i]);

}
}function eachWithCall(obj, callback) {
var i = 0;
var length = obj.length for (;
i <
length;
i++) {
value = callback.call(obj[i], i, obj[i]);

}
}var arr = Array.from({length: 1000000
}, (v, i) =>
i);
console.time('each')var i = 0;
each(arr, function(index, item){
i += item;

})console.timeEnd('each')console.time('eachWithCall')var j = 0;
eachWithCall(arr, function(index, item){
j += item;

})console.timeEnd('eachWithCall')複製程式碼

這裡顯示一次運算的結果:

效能比較
效能比較

each 函式和 eachWithCall 函式唯一的區別就是 eachWithCall 呼叫了 call,從結果我們可以推測出,call 會導致效能損失,但也正是 call 的存在,我們才能將 this 指向迴圈中當前的元素。

有舍有得吧。

專題系列

JavaScript專題系列目錄地址:github.com/mqyqingfeng…

JavaScript專題系列預計寫二十篇左右,主要研究日常開發中一些功能點的實現,比如防抖、節流、去重、型別判斷、拷貝、最值、扁平、柯里、遞迴、亂序、排序等,特點是研(chao)究(xi) underscore 和 jQuery 的實現方式。

如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。如果喜歡或者有所啟發,歡迎 star,對作者也是一種鼓勵。

來源:https://juejin.im/post/5982837bf265da3e3d123a60

相關文章