Iterator
iterator
遍历器在
JS
中内置了两种遍历方式for
、for...in
在
ES6
中新增了for...of
语句,该语句将会根据Iterator
的自定义遍历逻辑对数据进行遍历只有在具备遍历器接口的数据上可以使用for…of(数组。类数组对象天生就有遍历器接口)
var arr = [1, 2, 3, 4];
//遍历数组中的index
for (var some in arr) {
console.log(some);
}
//遍历数组中的value
for (var some of arr) {
console.log(some);
}
对象不具备遍历性,给对象添加可遍历属性
遍历器接口就是引用数据类型的一个属性(方法)——该方法决定了以什么样的方式进行遍历
Iterator
的作用:
- 为各种数据结构,提供一个统一的、简便的访问接口
- 使得数据结构的成员能够按某种次序排列
- 是
ES6
创造了一种新的遍历命令for...of
循环,Iterator接口主要供for...of
消费。
遍历器对象遍历过程:
- 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
- 第一次调用指针对象的
next
方法,可以将指针指向数据结构的第一个成员。- 第二次调用指针对象的
next
方法,指针就指向数据结构的第二个成员。- 不断调用指针对象的
next
方法,直到它指向数据结构的结束位置。
手写遍历器接口
遍历器结构
- 遍历器接口返回一个遍历器对象
- 接口是一个函数,函数返回一个遍历器对象
- 遍历器对象必须具有next的方法
- next方法必须返回两个东西——(value属性以及done)
- 遍历器接口不能用symbol方法来生成,必须使用
symbol.iterator
来生成,特殊的值,把他做成了symbol的静态属性;也就是说所有的遍历器接口的key
必须是symbol.iterator
以下为结构
o[Symbol.iterator] = function() {
// 遍历器对象,该对象必须拥有next方法
// 该方法必须返回一个具备value和done属性的对象
// 以上两点是强制要求
return {
next: function() {
return {value: "", done: false}
}
}
}
<script>
let o = {
name: "Tom",
age: 18,
gender: "male"
}
o[Symbol.iterator] = function() {
// 获取对象o中的key,并放入数组中
var keys = Object.getOwnPropertyNames(this);
// ["name", "age", "gender"]
// 想依次获取每一个key
var index = 0;
var self = this;
return {
next: function() {
// 获取完一次key,index就++一次
// done表示当前遍历是否完成
var value = self[keys[index++]]
//done: true; 遍历会立马停下来,并且前面的value就会被舍弃
//即 for...of 只保留done为false的value
return {value: value, done: index === keys.length && true}
}
}
}
for (var some of o) {
console.log(some);
}
</script>
原理
for...of
第一件事就是找有没有symbol.iterator
- 如果有
symbol.iterator
就说明有遍历器接口- 只要有接口,立马调用
symbol.iterator
,返回遍历器对象- 然后就会一次一次的调用
next
方法- 遍历器对象有什么用呢?
- 先生成遍历器对象
- 然后开始调用
next
的方法,即函数执行- 函数执行后,返回的值里面的
value
要赋值给some
- 然后根据
done
属性进行判断遍历是否完成
原理精简版
- 先查看是否具备遍历器接口,如果有则调用接口并返回遍历器对象,反之报错
- 调用遍历器对象中的
next
方法,将该方法返回的对象中的value
赋值给目标变量,根据返回的对象的done
属性来决定是否进行下次遍历for...of
只保留done
为false
的value
for...of
遍历出什么由自己决定,只要看返回的value
是什么
为什么对象不具备遍历器接口?因为对象的key是没有规律的
类数组对象使用for...of
借用array里的
<script>
var arrLike = {
0: "a",
1: "b",
2: "c",
length: 3
}
arrLike[Symbol.iterator] = Array.prototype[Symbol.iterator];
for (var some of o) {
console.log(some);
}
</script>
原生类数组遍历器接口
<script>
var arrLike = {
0: "a",
1: "b",
2: "c",
length: 3
}
arrLike[Symbol.iterator] = function() {
// 为了判断什么时候遍历结束
let len = this.length;
// 为了获取对象中的元素
var self = this;
// 循环初始条件
var index = 0
return {
next: function() {
var value = self[index++];
return {value: value, done: index > len && true}
}
}
}
for (var some of arrLike) {
console.log(some);
}
</script>
遍历类数组对象的偶数项
<script>
var arrLike = {
0: "a",
1: "b",
2: "c",
length: 3
}
arrLike[Symbol.iterator] = function() {
// 为了判断什么时候遍历结束
let len = this.length;
// 为了获取对象中的元素
var self = this;
// 循环初始条件
var index = 0
return {
next: function() {
var value = self[index];
index+=2;
return {value: value, done: index > (len+2) && true}
}
}
}
for (var some of arrLike) {
console.log(some);
}
</script>
Array.form
依赖遍历器接口
Array.from
可以把拥有遍历器接口的对象或者是类数组对象转换成数组- 把每次遍历的
value
放入到新数组中——遍历器对象返回什么,新数组中就是什么 - 类数组对象又具备遍历器接口,以遍历器返回的值为准
Array.from
是根据对象中的length进行操作
类似数组对象转数组
<script>
var arrLike = {
0: "Tom",
1: "Jerry",
2: "Tim",
3: "Tony",
length: 4
}
console.log(Array.from(arrLike));
//Array(4) [ "Tom", "Jerry", "Tim", "Tony" ]
</script>
具有遍历器接口的对象转数组
var obj = {
0: "Tom",
1: "Jerry",
2: "Tim",
3: "Tony"
}
obj[Symbol.iterator] = function() {
var keys = Object.getOwnPropertyNames(this);
var index = 0;
var self = this;
return {
next: function() {
var value = self[keys[index++]];
var done = index > keys.length && true
// 属性值与属性名相同时,es6对象语法糖
return {value, done}
}
}
}
var res = Array.from(obj);
console.log(res);
具有遍历器接口的类数组对象
var arrLike = {
0: "Tom",
1: "Jerry",
2: "Tim",
3: "Tony",
length: 4
}
//遍历偶数的key
arrLike[Symbol.iterator] = function() {
var keys = Object.getOwnPropertyNames(this);
var index = -2;
var self = this;
return {
next: function() {
var value = keys[index+=2];
var done = index > keys.length && true
// 属性值与属性名相同时,es6对象语法糖
return {value, done}
}
}
}
var res = Array.from(arrLike);
console.log(res);
原生Array.form
先看有没有遍历器接口
如果没有,再看有没有length,再根据length进行for循环进行遍历
Array.from1 = function(any){
let temp = [];
if(any[Symbol.iterator]){
for(let some of any){
temp.push(some);
}
}else if(any.length){
for(let i = 0; i < any.length; i++){
temp.push(any[i]);
}
}
return temp;
}