JavaScript中直接提供的循環,主要有如下幾種javascript
和其餘語言同樣,JavaScript中的while
循環有兩種形式:java
while (condition) { // 循環內容 } do { // 循環內容 } while (condition)
其中兩種形式的不一樣在於,對condition
判斷的位置不一樣,前者先判斷,再執行循環體;然後者先執行循環體一次,再進行條件判斷。因此結果的不一樣就是後者能將循環內容至少執行一次。node
for
循環的語法:數組
for (init; condition; step) { // 循環體代碼塊 }
init 語句 中的內容,會在循環代碼開始前執行一次,一般用來聲明一些變量。函數
condition 語句 會在每次循環開始時,進行判斷。若是第一次就不知足,則不會進入循環。工具
step 語句 會在每次循環結束時執行,一般是遞增或遞減condition中的某個變量。oop
var arr = [0, 10, 20, 30]; for (var j = 0; j < arr.length; j++) { console.log(arr[j]); } // 若是在循環體中不會改變數組的長度,用一個變量存儲數組長度是個更高效的選擇 for (var i = 0, l = arr.length; i < l; i++) { console.log(arr[i]); } var k = 0, length = arr.length; for (; k < length; k++) { console.log(arr[k]); }
for
循環中的init語句,一般用來聲明初始變量,其能夠有多個,用,
分隔便可。並且因爲此語句是在循環開始前執行,且只用執行一次,因此若是在外部已經聲明過要用的變量了,for
循環中的這個語句能夠直接省略,如上例中的第三個for
循環所示。post
for
循環的條件語句是在每次循環體開始前進行判斷,若是爲true
,則執行循環體內容,不然結束循環。當此語句省略時,表示不進行條件判斷,循環將一直執行,只有在循環中使用break
來跳出循環。this
for
循環的step語句,最多見的就是使用++
或者--
運算符的表達式,不過也可使用其餘任意值的,並且也能夠省略,換到循環體中進行改變賦值。prototype
for-in
循環是JavaScript中專門提供用來遍歷對象屬性的。
var zs = { name: 'zhang san', gender: 'male', age: 26 }; for (var key in zs) { console.log('%s : %s', key, zs[key]); } // name : zhang san // gender : male // age : 26
不過須要注意一點問題,請看以下示例:
function Person(name, gender) { this.name = name; this.gender = gender; } Person.prototype.sayHello = function() { console.log('Hello,I am', this.name, '. I\'m a', this.gender); }; var zs = new Person('zhang san', 'male'); for (var key in zs) { console.log('%s : %s',key, zs[key]); } // name : zhang san // gender : male // sayHello : function () { // console.log('Hello,I am', this.name, '. I\'m a', this.gender); // }
此次循環遍歷中,它還輸出了zs
原型鏈上的屬性sayHello
。這反應出一個問題,for - in
循環會遍歷整個原型鏈,雖然可使用hasOwnProperty
方法進行過濾僅獲取自身屬性,但其訪問的還是整個原型鏈,遍歷範圍較廣,因此其效率比較低,一般來講,其效率僅爲普通for
循環的1/7。
在JavaScript中,因爲數組也是對象,因此此方法也能夠用來遍歷數組。
var arr = [1, 2, 3, 4]; for (var i in arr) { console.log(typeof i); if (arr.hasOwnProperty(i)) console.log('arr[%s] : %d', i, arr[i]); else console.log('arr.%s : ', i, arr.[i]); } // string // arr[0] : 1 // string // arr[1] : 2 // string // arr[2] : 3 // string // arr[3] : 4 // string // arr.where : function ... // string // arr.groupBy : function ... // string // arr.has : function ...
這個輸出的結果或許不是你想象的樣子,然而他就是這樣。咱們認爲的索引,實際是對象的鍵名,因此其是String
類型。對於數組來講,和對象同樣,在其原型上擴展的方法where
、groupBy
、has
也都被輸出了。 或許會有疑問,數組不是有length
屬性嗎,爲何沒有輸出呢?緣由在於數組的length
屬性是不可枚舉屬性,for - in
循環不會訪問這些屬性。關於此的更多知識可參考MDN - 屬性的可枚舉性和全部權
Array 在 Javascript 中是一個對象,
Array
的索引是屬性名。事實上, Javascript 中的 「array
」 有些誤導性, Javascript 中的Array
並不像大部分其餘語言的數組。首先, Javascript 中的Array
在內存上並不連續,其次,Array
的索引並非指偏移量。實際上,Array
的索引也不是Number
類型,而是String
類型的。咱們能夠正確使用如arr[0]
的寫法的緣由是語言能夠自動將Number
類型的 0 轉換成String
類型的 "0" 。因此,在 Javascript 中歷來就沒有Array
的索引,而只有相似 "0" 、 "1" 等等的屬性。
for - in
循環本來就是用來遍歷對象的,用其來遍歷數組並不合適,不過也有例外的狀況,好比稀疏數組:
var arr = new Array(1000); arr[0] = 1; arr[99] = 3; arr[999] = 5; // for循環 for (var i = 0, l = arr.length; i < l; i++) { console.log('arr[%s]', i, arr[i]); } console.log('i :' , i); // ... // arr[0] 1 // ... // arr[99] 3 // ... // arr[999] 5 // i : 1000 // for - in 循環 var count = 0; for(var j in arr){ count ++ ; if(arr.hasOwnProperty(j)){ console.log('arr[%s]', j, arr[j]); } } console.log('count : ', count); // arr[0] 1 // arr[99] 3 // arr[999] 5 // i : 1000
直接使用普通的for
循環,循環次數爲數組長度1000次,而使用for - in
循環,僅僅循環了3次。
上面看起來循環的效率是高了很多,可是在前面已經說過了,for - in
的專職是對象的遍歷(相似的還有Object.keys()
),所以上面的方案並不是是一個完美的方案,更好的作法是用是數組自己的遍歷方法forEach
(forEach
爲ES5中新增,須要IE9+)。
var arr = new Array(1000); arr[0] = 1; arr[99] = 3; arr[999] = 5; var count = 0; arr.forEach(function(value, index) { count++; console.log(typeof index); console.log(index, value); }); console.log('count', count); // number // 0 1 // number // 99 3 // number // 999 5 // count 3
這樣就高效地進行了循環遍歷,並且數組的索引index
也被正確地識別爲了number
類型。
或許你會想到jQuery中提供的工具方法$.each()
和$.map()
或者實例jQuery對象的each
和map
方法,可是結果會讓你失望的,依舊會是循環1000次,如下是示例:
var count1 = 0; $.each(arr, function(index, value) { count1++; console.log(index, value); }); console.log('count1',count1); // count1 1000 var count2 = 0; $.map(arr,function(value,index){ count2++; console.log(index, value); }); console.log('count2',count2); // count2 1000
從上面對比來看Array.prototype.forEach
方法彷佛不錯,但其循環效率仍然不如普通的for
循環,不過優點在於在稀疏數組的處理。
不過forEach
方法,嚴格來算並不算是循環,緣由在於,它並不能響應循環應有的break
和continue
語句。準確地講,它是一個遍歷函數,對每一個元素執行指定的回調函數。
ES6中又新增了for - of
循環,它與for - in
循環相似,但又有所不一樣。
for - of
支持對數組和類數組對象進行循環,不支持普通對象的循環。
for - of
支持對字符串進行循環遍歷。
for - of
支持對Map
和 Set
(ES6 中新增的類型)對象遍歷。
for - of
不支持普通對象的循環遍歷。
// 數組 var arr = [0, 1, 2, 3, 4, 5]; for (let index of arr) { if (index === 1) continue; if (index === 4) break; console.log(typeof index); console.log(index, arr[index]); } // number // 0 0 // number // 2 2 // number // 3 3
當索引爲1時直接進行下一次循環,而當索引爲4時,直接退出循環,所以輸出如上結果,也一樣正確識別索引爲number
類型。
// 類數組 // 類數組 var divs = document.querySelectorAll('div'); for (let i of divs) { console.log(i.classList); } // ["container", "md-doc", value: "container md-doc"] // ["doc-cata", value: "doc-cata"] // ["nicescroll-rails", "nicescroll-rails-vr", value: "nicescroll-rails nicescroll-rails-vr"] // ["nicescroll-cursors", value: "nicescroll-cursors"] // ["nicescroll-rails", "nicescroll-rails-hr", value: "nicescroll-rails nicescroll-rails-hr"] // ["nicescroll-cursors", value: "nicescroll-cursors"] for (let k in divs) { console.log(k, divs[k].classList); } // "0" ["container", "md-doc", value: "container md-doc"] // "1" ["doc-cata", value: "doc-cata"] // "2" ["nicescroll-rails", "nicescroll-rails-vr", value: "nicescroll-rails nicescroll-rails-vr"] // "3" ["nicescroll-cursors", value: "nicescroll-cursors"] // "4" ["nicescroll-rails", "nicescroll-rails-hr", value: "nicescroll-rails nicescroll-rails-hr"] // "5" ["nicescroll-cursors", value: "nicescroll-cursors"] // length undefined // item undefined // keys undefined // values undefined // entries undefined // forEach undefined
在for - of
循環用於類數組對象的處理時,須要注意,of
關鍵字前面的變量即爲類數組對象中的鍵值,如上例所示,在for - of
循環中i
即表明着DOM nodelist 中的每一個對象,而在for - in
循環中k
表明的僅僅是對象的鍵名。
原文發表在個人博客JavaScript 循環,歡迎訪問!