歡迎大家的叫交流學習
for…of
for…of語句在可迭代對象(包括 Array,Map,Set,String,TypedArray,arguments 對象等等)上創建一個迭代循環,調用自定義迭代鉤子,併爲每個不同屬性的值執行語句
const array1 = ['a', 'b', 'c'];
for (const element of array1) {
console.log(element);
}
// expected output: "a"
// expected output: "b"
// expected output: "c"
一、語法
for (variable of iterable) {
//statements
}
- variable
在每次迭代中,將不同屬性的值分配給變量。- iterable
被迭代枚舉其屬性的對象。
二、示例
1. 迭代Array
數組原生具備iterator接口(即默認部署了Symbol.iterator屬性),for…of循環本質上就是調用這個接口產生的遍歷器,可以用下面的代碼證明。
const arr = ['red', 'green', 'blue'];
for(let v of arr) {
console.log(v); // red green blue
}
const obj = {};
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);
for(let v of obj) {
console.log(v); // red green blue
}
上面代碼中,空對象obj部署了數組arr的Symbol.iterator屬性,結果obj的for…of循環,產生了與arr完全一樣的結果。
for…of循環可以代替數組實例的forEach方法。
const arr = ['red', 'green', 'blue'];
arr.forEach(function (element, index) {
console.log(element); // red green blue
console.log(index); // 0 1 2
});
JavaScript 原有的for…in循環,只能獲得對象的鍵名,不能直接獲取鍵值。ES6 提供for…of循環,允許遍歷獲得鍵值。
var arr = ['a', 'b', 'c', 'd'];
for (let a in arr) {
console.log(a); // 0 1 2 3
}
for (let a of arr) {
console.log(a); // a b c d
}
上面代碼表明,for…in循環讀取鍵名,for…of循環讀取鍵值。如果要通過for…of循環,獲取數組的索引,可以藉助數組實例的entries方法和keys方法(參見《數組的擴展》一章)。
for…of循環調用遍歷器接口,數組的遍歷器接口只返回具有數字索引的屬性。這一點跟for…in循環也不一樣。
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
2. 迭代String
let iterable = "boo";
for (let value of iterable) {
console.log(value);
}
// "b"
// "o"
// "o"
3. 迭代 TypedArray
let iterable = new Uint8Array([0x00, 0xff]);
for (let value of iterable) {
console.log(value);
}
// 0
// 255
4. 迭代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
5. 迭代 Set
let iterable = new Set([1, 1, 2, 2, 3, 3]);
for (let value of iterable) {
console.log(value);
}
// 1
// 2
// 3
6. 迭代 arguments 對象
(function() {
for (let argument of arguments) {
console.log(argument);
}
})(1, 2, 3);
// 1
// 2
// 3
7. 迭代 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");
}
8. 關閉迭代器
對於for…of的循環,可以由break, throw continue 或return終止。在這些情況下,迭代器關閉。
function* foo(){
yield 1;
yield 2;
yield 3;
};
for (let o of foo()) {
console.log(o);
break; // closes iterator, triggers return
}
9. 迭代生成器
你還可以迭代一個生成器:
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;
}
10. 不要重用生成器
生成器不應該重用,即使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);
}
11. 迭代其他可迭代對象
你還可以迭代顯式實現可迭代協議的對象:
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,而不是任何對象的屬性。
四、規範
五、後記
喜歡我的可以關注我哦,相互學習。