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
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
原生具备 Iterator
接口的数据结构有:
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