小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
for...of
我们都知道for...of是ES6中添加的一个循环迭代的方法,用于遍历数组等,节省我们的代码工作量
let arr = [1, 2, 3, 4, 5, 6]
let obj = {
a: '1',
b: '2',
c: '3'
}
for (let val of arr) {
console.log(val)
}
// 1,2,3,4,5,6
for (let val of obj) {
console.log(val)
}
//TypeError: obj is not iterable
从上面我们可以看到,当使用for...of遍历Object的时候会报错,告诉我们obj不是一个iterable,什么是iterable,翻译过来叫做可迭代的,我们来看for...of在MDN上的定义
for...of语句在可迭代对象(包括Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
现在我们知道了,因为Object不是可迭代对象,所以for...of对它不好用,不能进行正常的遍历
那么为什么数组这一类可以,对象不行呢?它们之间差了什么导致Object不是可迭代对象?
console.dir(arr)
console.dir(obj)
通过打印发现,arr相比obj多了一个Symbol.iterable的方法,这个就是迭代的标识,有这个方法的就是可迭代对象,没有的就是不可迭代对象。
Symbol.iterable也可以叫做迭代器,那么我们能否根据for...of迭代数组的原理让obj也能够被迭代呢?手写一个迭代器,说干就干!
let obj = {
a: '1',
b: '2',
c: '3'
}
//首先给obj的原型添加一个Symbol.iterator方法
obj[Symbol.iterator] = function () {
//迭代器首先要有一个迭代协议,是怎么进行这个迭代进行规定,下面就是规定迭代obj的values值
let values = Object.values(obj)
//获取obj的所有values
let index = 0
//注意这里是固定写法,return返回的是一个对象,对象里面需要有一个next函数,next函数里面再返回一个对象
return {
next() {
if (index >= values.length) {
//当index大于等于values.length时,循环结束
return {
done: true
//done代表循环,true为循环结束,false为循环未结束,继续进行
}
} else {
return {
value: values[index++],
//value表示循环返回的值
done: false
}
}
}
}
}
//这时再使用for...of就能正常打印出来obj的values的值
for (let value of obj) {
console.log(value)
}
// '1','2','3'
通过上面代码我们简单实现了一个迭代器,那为什么for...of就可以打印出来呢,其实我们还可以再细致的拆分
let values = obj[Symbol.iterator]
console.log(values.next()) //{done:false,value:1}
console.log(values.next()) //{done:false,value:2}
console.log(values.next()) //{done:false,value:3}
console.log(values.next()) //{done:true}
从上面的打印我们可以看到,每次执行values.next()的结果都不一样,一直到循环结束,其实for...of就是帮助我们做了这样一个事情,只不过吧里面的value赋给for...of里面的val
这个迭代器是我们自己手写的,迭代协议也是我们自己定义的,刚才我们拿到的是values值,下面定义一个keys的
let obj = {
a: '1',
b: '2',
c: '3'
}
//首先给obj的原型添加一个Symbol.iterator方法
obj[Symbol.iterator] = function () {
//迭代器首先要有一个迭代协议,是怎么进行这个迭代进行规定,下面就是规定迭代obj的keys值
let keys = Object.keys(obj)
//获取obj的所有keys
let index = 0
//注意这里是固定写法,return返回的是一个对象,对象里面需要有一个next函数,next函数里面再返回一个对象
return {
next() {
if (index >= keys.length) {
//当index大于等于values.length时,循环结束
return {
done: true
//done代表循环,true为循环结束,false为循环未结束,继续进行
}
} else {
return {
value: keys[index++],
//value表示循环返回的值
done: false
}
}
}
}
}
//这时再使用for...of就能正常打印出来obj的values的值
for (let value of obj) {
console.log(value)
}
// a,b,c
如果想要得到keys和values,可以直接让values返回一个对象
return {
value:{
key:keys[index],
value:obj[keys[index++]]
}
}
for (let value of obj) {
console.log(value)
}
// {key:'a',value:'1'}
// {key:'b',value:'2'}
// {key:'c',value:'3'}