1. for…of循環
具有
iterator
接口,就可以用
for...of
循環周遊它的成員(屬性值
value
)。
for...of
循環可以使用的範圍包括數組、
Set
和
Map
結構、某些類似數組的對象、
Generator
對象,以及字元串。
for...of
循環調用周遊器接口,數組的周遊器接口隻傳回具有數字索引的屬性。對于普通的對象,
for...of
結構不能直接使用,會報錯,必須部署了
Iterator
接口後才能使用。可以中斷循環。
一個對象如果要具備可被
for...of
循環調用的
Iterator
接口,就必須在其
Symbol.iterator
的屬性上部署周遊器生成方法(或者原型鍊上的對象具有該方法)
PS: 周遊器對象根本特征就是具有
next
方法。每次調用
next
方法,都會傳回一個代表目前成員的資訊對象,具有
value
和
done
兩個屬性。
普通對象是不具備周遊器接口,例如:
let obj = {
name: "zl",
age: 21,
job: "coder"
};
for(let item of obj) {
console.log(item); //TypeError: obj is not iterable
}
為其部署周遊器生成方法:
let obj = {
name: "zl",
age: 21,
job: "coder",
[Symbol.iterator]() {
const self = this;
const keys = Object.keys(self); // [ 'name', 'age', 'job' ]
let index = 0;
return {
next() {
if(index<keys.length) {
return {
value: self[keys[index++]],
done: false
}
} else {
return {value: undefined, done: true}
}
}
}
}
};
for(let item of obj) {
console.log(item); //zl 21 coder
}
PS:
Object.keys()
:傳回給定對象所有可枚舉屬性的字元串數組。
因為
generator
函數傳回的就是疊代器,疊代器具有
next()
方法,是以使用
Generator
函數(周遊器對象生成函數)簡寫
Symbol.iterator
方法,可以簡寫如下:
let obj = {
name: "zl",
age: 21,
job: "coder",
* [Symbol.iterator]() {
const self = this;
const keys = Object.keys(self);
for(let index=0; index<keys.length; index++) {
yield self[keys[index]]
}
}
};
for(let item of obj) {
console.log(item); // zl 21 coder
}
原生具備
Iterator
接口的資料結構有:
- Array
- Map
- Set
- String
- TypedArray
- 函數的 arguments 對象
- NodeList 對象
- ES6 的數組、Set、Map 都部署了以下三個方法:
/entries()
/keys()
,調用後都傳回周遊器對象。values()
擴充:類數組及轉為數組的方法
兩者的差別:
⑴ 都擁有
length
屬性,其它屬性(索引)為非負整數(對象中的索引會被當做字元串來處理);
⑵ 類數組不具有數組所具有的方法;
⑶ 類數組是一個普通對象,而真實的數組是Array類型。
常見的類數組有: 函數的參數
arguments
, DOM 對象清單
Nodelist
(比如通過
document.querySelectorAll
得到的清單), jQuery 對象 (比如 $(“div”))。
類數組可以轉換為數組:
//第一種方法
Array.prototype.slice.call(arrayLike, start);
//第二種方法
[...arrayLike];
//第三種方法:
Array.from(arrayLike);
PS:任何定義了周遊器(
Iterator
)接口的對象,都可以用擴充運算符轉為真正的數組。
2. for…in循環
周遊對象自身的和繼承的可枚舉的屬性,也就是說會包括那些原型鍊上的屬性。如果想要僅疊代自身的屬性,那麼在使用
for...in
的同時還需要配合
getOwnPropertyNames()
或
hasOwnProperty()
。可以中斷循環。
3. forEach
隻能周遊數組,不能中斷,沒有傳回值(或認為傳回值是
undefined
,故無法鍊式調用)。傳遞的函數作為forEach()的第一個參數,然後forEach()使用三個參數調用該函數:數組元素、元素的索引和數組本身。看個例子:
//隻傳入了一個參數,數組元素的值。
var data = [1,2,3,4,5];
var sum = 0;
data.forEach(function (value) {
sum += value;
});
console.log(sum); //15
console.log(data) // [ 1, 2, 3, 4, 5 ]
// 傳入三個參數
var data = [1,2,3,4,5];
var sum = 0;
data.forEach(function (v, i, a) {
a[i] = v + 1;
});
console.log(data); // [ 2, 3, 4, 5, 6 ]
由上面兩個例子可見對元素進行操作的話是不會改變原數組的,但是直接對數組進行操作的話是會改變原數組的。
當數組項是複雜資料類型時,操作元素也會改變原數組:
let arr = [
{name: "zl"},
{age: 21}
];
arr.forEach((item) => {
item.job = "coder"
});
console.log(arr); //[ { name: 'zl', job: 'coder' }, { age: 21, job: 'coder' } ]
不能通過
break
中斷循環,但是用
try{}catch(){}
可以通過抛出異常跳出循環。捕獲異常機制本身的功能就是在出現異常的時候跳出try的代碼塊到
catch
裡處理異常。我們可以用
throw
方法手動抛出一個異常,這樣就跳出了
forEach
循環。
let arr = [0, 1, 2, 3, 4, 5, 6]
try{
arr.forEach((item) => {
if (item === 3) {
throw 'Jump out now!'//在這裡抛出異常
}
console.log(item)
})
} catch (e) {
console.log(e)
}
運作結果:
0
1
2
Jump out now!
4. map
隻能周遊數組,不能中斷,傳回值是修改後的數組。傳遞給
map
的函數的調用方式和傳遞給和
forEach()
的函數的調用方式是一樣的。但是傳遞給map()的函數應該有傳回值。注意,map()傳回的是新數組,對于原數組的改變與否參考
forEach()
。看一個簡單的例子:
var a = [1,2,3,4,5];
var b = a.map(function (x) {
return x * x
});
console.log(b);
console.log(a);
參考:
《JavaScript權威指南》
眷你:https://juejin.im/post/5bcb249a6fb9a05d212ed038#heading-0
劉小夕:https://juejin.im/post/5cab0c45f265da2513734390#heading-4