for in
一般用来遍历对象的键名,
也可以遍历数组和字符串,但是有以下缺点
- 不仅遍历数字键,还会遍历非数字键
let arr=[1,2,3]
arr[3]=true
for(let item in arr){
console.log(item)
}
// 0 1 2 true
- 只能遍历键名,而且类型为字符串
- in 不会区分自身属性与继承属性,最好与 hasOwnProperty 结合使用
- 在某些情况下,会以任意顺序遍历键名
for of
for of 只能遍历部署了 iteraotr 接口的数据结构
比如数组,字符串,map 和 set,还有 arguments 和 NodeList
而 for of 相比于 for in 来说,有以下优点
- 和 for in 一样简洁的写法,而没有上述缺点
- 可以与 break,return,continue 配合使用跳出循环
- 提供了遍历所有数据结构的统一操作接口
使用 for in 只能遍历对象的键名,而且遍历顺序不能得到保障,但是对象又没有原生部署遍历的接口,而且引用数组的 [Symbol.iterator] 也无效,我们可以自己实现一下,
let obj={
data:['hello','world'],
[Symbol.iterator](){
let self=this
let index=0
return {
next(){
if(index<self.data.length){
return {value:self.data[index++],done:false}
}
return {value:undefined,done:true}
}
}
}
}
for(let s of obj){
console.log(s)
}
// hello
// world
for of 每次循环都会去调用 [Symbol.iterator] 函数,此函数返回一个含有 next 方法的对象,通过调用 next 来输出相应的值
也可以使用 Object.keys 来返回一个遍历器对象达到遍历对象的方法
let obj={
name:'frank',
age:18
}
for(let key of Object.keys(obj)){
console.log(`${key}:${obj[key]}`)
}
现在看来,想要使用 for of 循环,对应的数据结构必须拥有 iterator 接口,没有的话就要想办法创造,entries,keys 和 values 方法都可以生成遍历器对象,只是遍历位置不同
还可以使用 generator 来实现,代码更为简洁
let obj={
a:0,
b:1,
c:3
}
function* entries(obj){
for(let key of Object.keys(obj)){
yield [key,obj[key]]
}
}
for(let [key,value] of entries(obj)){
console.log(`${key}:${value}`)
}
现在我们知道,for of 首先会先调用对应的 [Symbol.iterator] 方法,此方法返回一个具有 next 方法的对象,然后每次都会调用 next 方法来得到对应的 value,这也是 generator 函数能够执行的原因