一、迭代器是什么
在JavaScript中,迭代器也是一个具体的对象,这个对象需要符合迭代器协议(iterator protocol)
1. 迭代的定义:从一个数据集合中按照一定的顺序,不断取出数据的过程称为迭代
2. 迭代和遍历的区别
迭代:迭代强调的是依次取数据的过程,并不保证取多少,也不保证把所有的数据都取完
遍历:遍历强调的是要把整个数据依次全部取出
迭代器(iterable)是一个接口! 可以遍历具有该数据结构的对象,为各种容器提供了公共的操作接口,隔离对容器的遍历操作和底层实现,从而解耦
可迭代对象(Iterable)是指实现了可迭代协议(Iterable Protocol)的对象
。换句话说,可迭代对象是具有 Symbol.iterator 方法的对象,该方法返回一个迭代器对象。,对象中有一个next方法,next方法的返回值是一个对象,该对象中包含一个布尔值done:代表当前是否迭代完成和一个当前正在迭代的元素值value
let index = 0
const days= ["星期一", "星期二", "星期三", "星期四",]
const daysIterator = {
next: function () {
if (index < days.length) {
return { done: false, value: days[index++] }
} else {
return { done: true, value: undefined }
}
}
}
console.log(daysIterator.next()) // {done: false, value: '星期一'}
console.log(daysIterator.next()) // {done: false, value: '星期二'}
console.log(daysIterator.next()) // {done: false, value: '星期三'}
console.log(daysIterator.next()) // {done: false, value: '星期四'}
console.log(daysIterator.next()) // {done: true, value: undefined}
console.log(daysIterator.next()) // {done: true, value: undefined}
二、可迭代对象
- 通过可迭代对象中的迭代器工厂函数
Symbol.iterator
来生成迭代器。 - 如果我们希望一个对象可以迭代,必须为对象添加一个名为
Symbol.iterator
的方法(一个专门使对象可迭代的内建Symbol) - 当一个对象实现了iterable接口时,它就是一个可迭代对象
- 这个对象的要求是必须实现iterator 方法,在代码中我们使用 Symbol.iterator 访问该属性
注意:
1):如果可迭代对象在迭代期间被修改了,迭代器得到的结果也会是修改后的。
2):当我们迭代到 done: true
之后,再调用next
是不是会报错的,到的结果一直都会是 { value: undefined, done: true }
。
3):对象并是没有实现这个迭代协议,所以不能使用for of进行遍历
4):常见的可迭代对象包括数组、Set、Map、String,Array,Set,arguments,NodeList 等,它们都实现了可迭代协议,因此可以直接在 for…of 循环中使用
5):Array 实例实现了iterable接口迭代协议
三、给对象添加iterator接口
目的:使用for of遍历对象
只需给要被遍历的对象自身或者原型上添加[Symbol.iterator]属性,当使用for of进行遍历时会自动执行该函数(下面代码是将该属性挂载在对象自身)
const infos = {
name: "xiaozhi",
age: 22,
height: 1.77,
[Symbol.iterator]: function() {
const entries = Object.values(this)
let index = 0
const iterator = {
next: function() {
if (index < entries.length) {
return { done: false, value: values[index++] }
} else {
return { done: true }
}
}
}
return iterator
}
}
//【遍历对象】 可迭对象可以进行for of操作
for (const item of infos) {
console.log(value)
}
前面说了只有当一个数据结构具有iterator接口时,才能被for of遍历,但是对象本身不具备该接口,上面代码我们在infos对象自身添加了一个[Symbol.iterator]属性该属性对应一个函数,于是在使用for of进行遍历时,会自动调用该函数执行函数体,在函数体中通过Object构造函数上的 values() 方法是遍历this对象的每一个值得到一个可迭代的数组,然后返回一个next方法,在next方法内部通过闭包实现了局部变量的私有化,当使用for of遍历时会将数组中的值依次赋值给item( item = next().value )
四:一道字节面试题
字节面试官问出这样一道面试题:let [a, b, c] = { a: 1, b: 2, c: 3 },请把它成功解构。
大家都明白对象的解构左边应该是花括号,数组的结构左边应该是中括号,而这道题相反
所以对象为什么能解构?
我们都知道想遍历对象有2种方式:
1.for in
2.Object.keys()先获取key的数组,然后使用数组遍历方式获取key对应的value
所以要想对对象进行解构,要先让对象具有[Symbol.iterator]属性,因此可以将该属性添加到Object构造函数的原型对象上通过原型链让所有实例对象访问到该属性,
Object.prototype[Symbol.iterator] = function() {
return Object.values(this)[Symbol.iterator]()
}
var [a,b] = {a:1,b:2} //[1,2] //new Object()
console.log(a, b);
而我们知道Object构造函数上的values是遍历this对象的每一个值,以数组的形式输出,然后数组再调用迭代器属性。最终就构造了这个对象身上的迭代器,因此我们可以让它正常解构。