大家都知道在JavaScript中提供了兩種方式迭代對象:
一、for循環
不足:
在於每次循環的時候數組的長度都要去獲取;
終止條件要明確;
在for循環中,你可以循環取得數組或是數組類似對象的值,譬如arguments和HTMLCollection對象。通常的循環形式如下:
// 次佳的循環 for (var i = 0; i < myarray.length; i++) { // 使用myarray[i]做點什麼 }
這種形式的循環的不足在於每次循環的時候數組的長度都要去獲取下。這回降低你的代碼性能,尤其當myarray不是數組,而是一個HTMLCollection對象的時候。
HTMLCollections指的是DOM方法返回的對象,例如:
document.getElementsByName()
document.getElementsByClassName()
document.getElementsByTagName()
還有其他一些HTMLCollections,這些是在DOM標准之前引進並且現在還在使用的。有:
document.images: 頁面上所有的圖片元素
document.links : 所有a標簽元素
document.forms : 所有表單
document.forms[0].elements : 頁面上第一個表單中的所有域
集合的麻煩在於它們實時查詢基本文檔(HTML頁面)。這意味著每次你訪問任何集合的長度,你要實時查詢DOM,而DOM操作一般都是比較昂貴的。
這就是為什麼當你循環獲取值時,緩存數組(或集合)的長度是比較好的形式,正如下面代碼顯示的:
for (var i = 0, max = myarray.length; i < max; i++) { // 使用myarray[i]做點什麼 }
這樣,在這個循環過程中,你只檢索了一次長度值。
在所有浏覽器下,循環獲取內容時緩存HTMLCollections的長度是更快的,2倍(Safari3)到190倍(IE7)之間。//此數據貌似很老
注意到,當你明確想要修改循環中的集合的時候(例如,添加更多的DOM元素),你可能更喜歡長度更新而不是常量。
伴隨著單var形式,你可以把變量從循環中提出來,就像下面這樣:
function looper() { var i = 0, max, myarray = []; // ... for (i = 0, max = myarray.length; i < max; i++) { // 使用myarray[i]做點什麼 } }
這種形式具有一致性的好處,因為你堅持了單一var形式。不足在於當重構代碼的時候,復制和粘貼整個循環有點困難。例如,你從一個函數復制了一個循環到另一個函數,你不得不去確定你能夠把i和max引入新的函數(如果在這裡沒有用的話,很有可能你要從原函數中把它們刪掉)。
最後一個需要對循環進行調整的是使用下面表達式之一來替換i++。
i = i + 1 i += 1
JSLint提示您這樣做,原因是++和–-促進了“過分棘手(excessive trickiness)”。如果你直接無視它,JSLint的plusplus選項會是false(默認是default)。
兩種變化的形式:
//第一種變化的形式: var i, myarray = []; for (i = myarray.length; i–-;) { // 使用myarray[i]做點什麼 } //第二種使用while循環: var myarray = [], i = myarray.length; while (i–-) { // 使用myarray[i]做點什麼 }
這些小的改進只體現在性能上,此外JSLint會對使用i–-加以抱怨。
二、for …in 循環—也被稱為“枚舉”
for …in 循環經常用來迭代對象的屬性或數組的每個元素,for…in循環中的循環計數器是字符串,而不是數字。它包含當前屬性的名稱或當前數組元素的索引。 下面直接上幾個例子:
當遍歷一個對象的時候,變量 i 也就是循環計數器 為 對象的屬性名 :
//使用for..in循環遍歷對象屬性 varperson={ name: "Admin", age: 21, address:"shandong" }; for(var i in person){ console.log(i); }
執行結果為:
name
age
address
當遍歷一個數組的時候,變量 i 也就是循環計數器 為 當前數組元素的索引 :
//使用for..in循環遍歷數組 vararray = ["admin","manager","db"] for(vari in array){ console.log(i); }
執行結果:
0
1
2
但是,現在看來for .. in循環還挺好用啊,不過,別高興太早,看看下面的例子:
var array =["admin","manager","db"]; //給Array的原型添加一個name屬性 Array.prototype.name= "zhangsan"; for(var i in array){ alert(array[i]); }
運行結果:
admin
manager
db
zhangsan
咦,奇觀了,怎麼平白無故的冒出來一個zhangsan
現在,再看看使用 for循環會怎樣?
vararray = ["admin","manager","db"]; //給Array的原型添加一個name屬性 Array.prototype.name = "zhangsan"; for(var i =0 ; i<array.length; i++){ alert(array[i]); };
運行結果:
admin
manager
db
哦, 現在明白了,for..in循環會把某個類型的原型(prototype)中方法與屬性給遍歷出來,所以這可能會導致代碼中出現意外的錯誤。為了避免這個問題,我們可以使用對象的hasOwnProperty()方法來避免這個問題,如果對象的屬性或方法是非繼承的,那麼hasOwnProperty() 方法返回true。即這裡的檢查不涉及從其他對象繼承的屬性和方法,只會檢查在特定對象自身中直接創建的屬性。
vararray = ["admin","manager","db"]; Array.prototype.name= "zhangshan"; for(var i in array){ //如果不是該對象自身直接創建的屬性(也就是該屬//性是原型中的屬性),則跳過顯示 if(array.hasOwnProperty(i)){ alert(array[i]); } }
運行結果:
admin
manager
db
另外一種使用hasOwnProperty()的形式是取消Object.prototype上的方法。像這樣:
// 對象 var man = { hands: 2, legs: 2, heads: 1 }; for (var i in man) { if (Object.prototype.hasOwnProperty.call(man, i)) { // 過濾 console.log(i, ":", man[i]); } }
其好處在於在man對象重新定義hasOwnProperty情況下避免命名沖突。也避免了長屬性查找對象的所有方法,你可以使用局部變量“緩存”它。
var i, hasOwn = Object.prototype.hasOwnProperty; for (i in man) { if (hasOwn.call(man, i)) { // 過濾 console.log(i, ":", man[i]); } }
嚴格來說,不使用hasOwnProperty()並不是一個錯誤。根據任務以及你對代碼的自信程度,你可以跳過它以提高些許的循環速度。但是當你對當前對象內容(和其原型鏈)不確定的時候,添加hasOwnProperty()更加保險些。
格式化的變化(通不過JSLint)會直接忽略掉花括號,把if語句放到同一行上。其優點在於循環語句讀起來就像一個完整的想法(每個元素都有一個自己的屬性”X”,使用”X”干點什麼):
// 警告: 通不過JSLint檢測 var i, hasOwn = Object.prototype.hasOwnProperty; for (i in man) if (hasOwn.call(man, i)) { // 過濾 console.log(i, ":", man[i]); }
以上就是介紹了JavaScript提供的兩種方式迭代對象:for循環和for...in循環,希望這篇文章對大家的學習有所幫助。