js之 for循環
普通for 循環
語法
for ([initialization]; [condition]; [final-expression]) statement
-
initialization
-
一個表達式 (包含賦值語句) 或者變量聲明。典型地被用於初始化一個計數器。該表達式可以使用
var
關鍵字聲明新的變量。初始化中的變量不是該循環的局部變量,而是與for
循環處在同樣的作用域中。該表達式的結果無意義。 -
condition
-
一個條件表達式被用於確定每一次循環是否能被執行。如果該表達式的結果為true,
statement
將被執行。 這個表達式是可選的。如果被忽略,那么就被認為永遠為真。如果計算結果為假,那么執行流程將被跳到for
語句結構后面的第一條語句。 -
final-expression
-
每次循環的最后都要執行的表達式。執行時機是在下一次
condition
的計算之前。通常被用於更新或者遞增計數器變量。 -
statement
-
只要
condition
的結果為true就會被執行的語句。 要在循環體內執行多條語句,使用一個 塊語句({ ... }
)來包含要執行的語句。沒有任何語句要執行,使用一個空語句(;
)。
事例
for (var i = 0; i < 9; i++) { console.log(i); // more statements }
for await...of
語法
for await (variable of iterable) { statement }variable
在每次迭代時,將不同屬性的值分配給變量。iterable
迭代其可迭代屬性的對象。
事例:
async function* asyncGenerator() { var i = 0; while (i < 3) { yield i++; } } (async function() { for await (num of asyncGenerator()) { console.log(num); } })(); // 0 // 1 // 2
for each...in
語法
for each (variable in object) { statement }
-
variable
-
用來遍歷屬性值的變量,前面的
var
關鍵字是可選的.該變量是函數的局部變量而不是語句塊的局部變量.
-
object
- 該對象的屬性值會被遍歷.
-
statement
-
遍歷屬性值時執行的語句. 如果想要執行多條語句, 請用(
{ ... }
) 將多條語句括住.
事例:
var sum = 0; var obj = {prop1: 5, prop2: 13, prop3: 8}; for each (var item in obj) { sum += item; } print(sum); // 輸出"26",也就是5+13+8的值
警告:永遠不要使用for each...in語句遍歷數組,僅用來遍歷常規對象
for...in
語法
for (variable in object) {...}
-
variable
- 在每次迭代時,將不同的屬性名分配給變量。
-
object
- 被迭代枚舉其屬性的對象
-
描述
for...in
循環只遍歷可枚舉屬性。像Array
和Object
使用內置構造函數所創建的對象都會繼承自Object.prototype
和String.prototype
的不可枚舉屬性,例如String
的indexOf()
方法或Object
的toString()
方法。循環將遍歷對象本身的所有可枚舉屬性,以及對象從其構造函數原型中繼承的屬性(更接近原型鏈中對象的屬性覆蓋原型屬性)。刪除,添加或者修改屬性
for...in
循環以任意序迭代一個對象的屬性(請參閱delete
運算符,了解為什么不能依賴於迭代的表面有序性,至少在跨瀏覽器設置中)。如果一個屬性在一次迭代中被修改,在稍后被訪問,其在循環中的值是其在稍后時間的值。一個在被訪問之前已經被刪除的屬性將不會在之后被訪問。在迭代進行時被添加到對象的屬性,可能在之后的迭代被訪問,也可能被忽略。通常,在迭代過程中最好不要在對象上進行添加、修改或者刪除屬性的操作,除非是對當前正在被訪問的屬性。這里並不保證是否一個被添加的屬性在迭代過程中會被訪問到,不保證一個修改后的屬性(除非是正在被訪問的)會在修改前或者修改后被訪問,不保證一個被刪除的屬性將會在它被刪除之前被訪問。數組迭代和
for...in
提示:
for...in
不應該用於迭代一個Array
,其中索引順序很重要。數組索引只是具有整數名稱的枚舉屬性,並且與通用對象屬性相同。不能保證
for ... in
將以任何特定的順序返回索引。for ... in
循環語句將返回所有可枚舉屬性,包括非整數類型的名稱和繼承的那些。因為迭代的順序是依賴於執行環境的,所以數組遍歷不一定按次序訪問元素。因此當迭代訪問順序很重要的數組時,最好用整數索引去進行
for
循環(或者使用Array.prototype.forEach()
或for...of
循環)。僅迭代自身的屬性
如果你只要考慮對象本身的屬性,而不是它的原型,那么使用
getOwnPropertyNames()
或執行hasOwnProperty()
來確定某屬性是否是對象本身的屬性(也能使用propertyIsEnumerable
)。或者,如果你知道不會有任何外部代碼干擾,您可以使用檢查方法擴展內置原型。事例
var obj = {a:1, b:2, c:3}; for (var prop in obj) { console.log("obj." + prop + " = " + obj[prop]); } // Output: // "obj.a = 1" // "obj.b = 2" // "obj.c = 3"
var triangle = {a: 1, b: 2, c: 3}; function ColoredTriangle() { this.color = 'red'; } ColoredTriangle.prototype = triangle; var obj = new ColoredTriangle(); for (var prop in obj) { if (obj.hasOwnProperty(prop)) { console.log(`obj.${prop} = ${obj[prop]}`); } } // Output: // "obj.color = red"
for...of
語法
for (variable of iterable) { //statements }variable
在每次迭代中,將不同屬性的值分配給變量。iterable
被迭代枚舉其屬性的對象。
示例
迭代Array
let iterable = [10, 20, 30]; for (let value of iterable) { value += 1; console.log(value); } // 11 // 21 // 31
如果你不想修改語句塊中的變量 , 也可以使用const
代替let
。
let iterable = [10, 20, 30]; for (const value of iterable) { console.log(value); } // 10 // 20 // 30
迭代String
let iterable = "boo"; for (let value of iterable) { console.log(value); } // "b" // "o" // "o"
迭代 TypedArray
節
let iterable = new Uint8Array([0x00, 0xff]); for (let value of iterable) { console.log(value); } // 0 // 255
迭代Map
let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]); for (let entry of iterable) { console.log(entry); } // ["a", 1] // ["b", 2] // ["c", 3] for (let [key, value] of iterable) { console.log(value); } // 1 // 2 // 3
迭代 Set
let iterable = new Set([1, 1, 2, 2, 3, 3]); for (let value of iterable) { console.log(value); } // 1 // 2 // 3
迭代 arguments
對象
(function() { for (let argument of arguments) { console.log(argument); } })(1, 2, 3); // 1 // 2 // 3
迭代 DOM 集合
迭代 DOM 元素集合,比如一個NodeList
對象:下面的例子演示給每一個 article 標簽內的 p 標簽添加一個 "read
" 類。
//注意:這只能在實現了NodeList.prototype[Symbol.iterator]的平台上運行 let articleParagraphs = document.querySelectorAll("article > p"); for (let paragraph of articleParagraphs) { paragraph.classList.add("read"); }
關閉迭代器
對於for...of
的循環,可以由break
, throw
或return
終止。在這些情況下,迭代器關閉。
function* foo(){ yield 1; yield 2; yield 3; }; for (let o of foo()) { console.log(o); break; // closes iterator, triggers return }
迭代生成器
你還可以迭代一個生成器:
function* fibonacci() { // 一個生成器函數 let [prev, curr] = [0, 1]; for (;;) { // while (true) { [prev, curr] = [curr, prev + curr]; yield curr; } } for (let n of fibonacci()) { console.log(n); // 當n大於1000時跳出循環 if (n >= 1000) break; }
不要重用生成器
生成器不應該重用,即使for...of
循環的提前終止,例如通過break
關鍵字。在退出循環后,生成器關閉,並嘗試再次迭代,不會產生任何進一步的結果。
var gen = (function *(){ yield 1; yield 2; yield 3; })(); for (let o of gen) { console.log(o); break;//關閉生成器 } //生成器不應該重用,以下沒有意義! for (let o of gen) { console.log(o); }
迭代其他可迭代對象
你還可以迭代顯式實現可迭代協議的對象:
var iterable = { [Symbol.iterator]() { return { i: 0, next() { if (this.i < 3) { return { value: this.i++, done: false }; } return { value: undefined, done: true }; } }; } }; for (var value of iterable) { console.log(value); } // 0 // 1 // 2
for...of
與for...in
的區別
無論是for...in
還是for...of
語句都是迭代一些東西。它們之間的主要區別在於它們的迭代方式。
for...in
語句以原始插入順序迭代對象的可枚舉屬性。
for...of
語句遍歷可迭代對象定義要迭代的數據。
以下示例顯示了與Array
一起使用時,for...of
循環和for...in
循環之間的區別。
Object.prototype.objCustom = function() {}; Array.prototype.arrCustom = function() {}; let iterable = [3, 5, 7]; iterable.foo = 'hello'; for (let i in iterable) { console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom" } for (let i in iterable) { if (iterable.hasOwnProperty(i)) { console.log(i); // logs 0, 1, 2, "foo" } } for (let i of iterable) { console.log(i); // logs 3, 5, 7 } Object.prototype.objCustom = function() {}; Array.prototype.arrCustom = function() {}; let iterable = [3, 5, 7]; iterable.foo = 'hello';
每個對象將繼承objCustom
屬性,並且作為Array
的每個對象將繼承arrCustom
屬性,因為將這些屬性添加到Object.prototype
和Array.prototype
。由於繼承和原型鏈,對象iterable
繼承屬性objCustom
和arrCustom
。
for (let i in iterable) { console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom" }
此循環僅以原始插入順序記錄iterable
對象的可枚舉屬性。它不記錄數組元素3
, 5
, 7
或hello
,因為這些不是枚舉屬性。但是它記錄了數組索引以及arrCustom
和objCustom
。如果你不知道為什么這些屬性被迭代,array iteration and for...in
中有更多解釋。
for (let i in iterable) { if (iterable.hasOwnProperty(i)) { console.log(i); // logs 0, 1, 2, "foo" } }
這個循環類似於第一個,但是它使用hasOwnProperty()
來檢查,如果找到的枚舉屬性是對象自己的(不是繼承的)。如果是,該屬性被記錄。記錄的屬性是0
, 1
, 2
和foo
,因為它們是自身的屬性(不是繼承的)。屬性arrCustom
和objCustom
不會被記錄,因為它們是繼承的。
for (let i of iterable) { console.log(i); // logs 3, 5, 7 }
該循環迭代並記錄iterable
作為可迭代對象定義的迭代值,這些是數組元素 3
, 5
, 7
,而不是任何對象的屬性。
摘取自MDN