摘要:語法為回調函數擁有兩個參數第一個為對象的成員或數組的索引,第二個為對應變量或內容。但是對于的函數,如果需要退出循環可使回調函數返回,其它返回值將被忽略。
each介紹JavaScript 專題系列第十一篇,講解 jQuery 通用遍歷方法 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 + index var 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專題系列目錄地址:https://github.com/mqyqingfeng/Blog。
JavaScript專題系列預計寫二十篇左右,主要研究日常開發中一些功能點的實現,比如防抖、節流、去重、類型判斷、拷貝、最值、扁平、柯里、遞歸、亂序、排序等,特點是研(chao)究(xi) underscore 和 jQuery 的實現方式。
如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。如果喜歡或者有所啟發,歡迎 star,對作者也是一種鼓勵。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84603.html
摘要:專題系列共計篇,主要研究日常開發中一些功能點的實現,比如防抖節流去重類型判斷拷貝最值扁平柯里遞歸亂序排序等,特點是研究專題之函數組合專題系列第十六篇,講解函數組合,并且使用柯里化和函數組合實現模式需求我們需要寫一個函數,輸入,返回。 JavaScript 專題之從零實現 jQuery 的 extend JavaScritp 專題系列第七篇,講解如何從零實現一個 jQuery 的 ext...
摘要:寫在前面專題系列是我寫的第二個系列,第一個系列是深入系列。專題系列自月日發布第一篇文章,到月日發布最后一篇,感謝各位朋友的收藏點贊,鼓勵指正。 寫在前面 JavaScript 專題系列是我寫的第二個系列,第一個系列是 JavaScript 深入系列。 JavaScript 專題系列共計 20 篇,主要研究日常開發中一些功能點的實現,比如防抖、節流、去重、類型判斷、拷貝、最值、扁平、柯里...
摘要:因為在微信小程序中,和都是,加上又強制使用嚴格模式,為,掛載就會發生錯誤,所以就有人又發了一個,代碼變成了這就是現在的樣子。 前言 在 《JavaScript 專題系列》 中,我們寫了很多的功能函數,比如防抖、節流、去重、類型判斷、扁平數組、深淺拷貝、查找數組元素、通用遍歷、柯里化、函數組合、函數記憶、亂序等,可以我們該如何組織這些函數,形成自己的一個工具函數庫呢?這個時候,我們就要借...
摘要:一個經常會看到的函數的實現為第一版我們可以這樣使用或者或者已經有柯里化的感覺了,但是還沒有達到要求,不過我們可以把這個函數用作輔助函數,幫助我們寫真正的函數。 JavaScript 專題系列第十三篇,講解函數柯里化以及如何實現一個 curry 函數 定義 維基百科中對柯里化 (Currying) 的定義為: In mathematics and computer science, cu...
摘要:前端日報精選專題之通用遍歷方法的實現深入了解的子組件上最流行的項目再聊移動端頁面的適配譯盒子模型實踐教程中文全棧第天數據驅動龍云全棧譯年開發趨勢瘋狂的技術宅在翻譯譯閉包并不神秘前端心得拼多多前端筆試個人文章容器技術方 2017-08-03 前端日報 精選 JavaScript專題之jQuery通用遍歷方法each的實現深入了解React的子組件GitHub上最流行的Top 10 Jav...
閱讀 3477·2021-09-06 15:13
閱讀 1527·2021-09-02 10:19
閱讀 2473·2019-08-30 15:52
閱讀 918·2019-08-29 15:25
閱讀 1565·2019-08-26 18:36
閱讀 495·2019-08-26 13:23
閱讀 1331·2019-08-26 10:46
閱讀 3498·2019-08-26 10:41