- for..of能遍历对象吗?
- for...in和for...of的区别是什么?
- 什么是可枚举以及可迭代?
这篇笔记就是用来彻底搞清楚这几个问题,用于备忘。
for...in
官方解释:for...in以任意顺序遍历一个对象的可枚举属性; 注:
- Symbol属性除外。
- 既可以遍历对象自身的可枚举属性,又能遍历到对象原型上的可枚举属性(可使用getOwnPropertyNames()或hasOwnProperty()来确定属性是否是对象本身的属性)。
- for...in是为遍历对象的属性而构建的,不建议与数组一起使用。数组可用forEach和for...of。
什么是可枚举属性?
可枚举属性是指那些内部“可枚举”标志设置为true的属性;其值会根据初始值的方式有所不同:
- 直接通过等号赋值的变量,其对应的标识enumerable默认为true;
let o = {'name':'xixinxixin'}
console.log(o.propertyIsEnumerable('name'))//true
- 通过Object.defineProperty等定义的属性,该标识默认为false。
let o = {}
Object.defineProperty(o,'name',{value:'xixinxixin'})
console.log(o.propertyIsEnumerable('name'))//false
for...of
for...of是ES6引入来遍历所有数据结构的统一方法,其中这里的所有数据结构具有可迭代数据结构。
可迭代
可迭代对象是指:
- 对象内置或者自己实现了一个@@iterator方法,可通过常量Symbol.iterator访问该属性;
- 可迭代对象是具有 Symbol.iterator 属性的对象。
- 一个数据只要部署了Symbol.iterator,就具有了iterator接口,就可以使用for...of循环遍历它的成员。即for...of循环内部调用的数据结构为Symbol.iterator方法。
Symbol.iterator
Symbol.iterator为每一个对象定义了默认的迭代器。该迭代器可以被for...of循环使用。
- 遍历器(Iterator)就是这样一种机制。它是一种借口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署了Iterator借口,就可以完成遍历操作(即依次处理该数据结构的所有成员)
- Iterator的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历for...of循环、Iterator接口主要供for...of循环使用。 此外,[Symbol.iterator]是一个无参数的函数,返回给定对象的迭代器。那什么是迭代器对象?以及为什么会出现迭代器?👇
为什么会出现迭代器?
一般在使用JS编程时,你可能会写过或看到过如下类似的代码:
var colors = ["red", "green", "blue"];
for (var i = 0, len = colors.length; i < len; i++) {
console.log(colors[i]);
}
在for循环中,需要一个变量i来保持指向集合中的某个值。如果当集合嵌套多层,比较复杂的时候,需要使用多个变量来遍历;嵌套越多或者对象越复杂那么维护的变量越多,出错的几率也就越大;而迭代器的出现就是来解决这类问题。
迭代器对象
-
迭代器对象是具有特定迭代接口的对象;
-
所有的迭代器对象都具有一个next方法,next方法返回一个结果对象;同时注意,next()方法必须是一个无参数函数,且返回一个拥有以下两个属性的对象:
- done:如果迭代器可以产生序列的下一个值,则为false。如果迭代器已将序列迭代完毕,则为true。在这种情况下,value是可选的,如果他依然存在,即迭代结束之后的默认值。
- value:迭代器返回的任何JavaScript值。done为true时可省略
-
迭代器保持着一个指向集合中某个值的内部指针,每次调用next方法,都会返回一个应有的值。 注:next()方法必须返回一个对象,该对象具有两个属性:done和value,如果返回一个非对象值(false或undefined)则会抛出一个TypeError异常
let o={};//此时不是可迭代的
o[Symbol.iterator]=function(){
return {
next:function(){
if(this.index<3){
return {
value:this.index++,
done:false
}
}else{
return {
done:true
}}
},
index:0
}
}
//现在对象o是可遍历的,可以被for...of以及展开语法操作...
for(let key of o){
console.log(o)
}//logs:0,1,2
综上所述:一个返回特定迭代接口的对象,就是可迭代的对象;这个特定接口返回一个包含next方法的迭代器对象。
在前面我提到了在 for 循环中跟踪索引的问题。迭代器的出现是该问题解决方案的第一部分。而for-of循环是第二部分,因为它不需要完全跟踪集合中的索引,让你可以自由地专注于处理集合的内容。 for...of在可迭代对象上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句 。
let values = [1, 2, 3];
for (let num of values) {
console.log(num);//1,2,3
}
通过上面例子,我们可以看到我们遍历数组时,无需关心索引,只需关注数组中的内容。
注:
- 可迭代对象(包括Array,Map,Set,String,TypedArray,arguments对象等等);
- 生成器创建的所有迭代器也是可迭代的,因为生成器默认分配 Symbol.iterator 属性。
- 普通创建的对象是不能被for...of迭代的,除非将对象构建成可迭代对象;
总结
- 可迭代对象本身或原型链上实现了
[Symbol.iterable]方法,可以根据typeof obj[Symbol.iterable] === "function"判断是否是可迭代对象 - 迭代方法返回迭代器对象,一个带有 next 方法的对象,next 方法返回 done 和 value 构成的迭代信息
- for...in和for...of的区别在于迭代的方式。前者以任意顺序遍历对象的可枚举属性。后者遍历可迭代对象定义要迭代的数据。