ES6学习第五篇--迭代器(Iterator)

迭代器的概念

JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
扩展知识:阮一峰解说迭代器

迭代器的作用

Iterator 的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是 ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of消费。
for-of的原理,每次遍历都会调用该对象的[Symbol.iterator]属性的next方法,当返回{value: undefined, done: true}后,表示遍历结束。

const array1 = ['a', 'b', 'c'];
const iterator = array1.values();
console.log(iterator.next());  // Object { value: "a", done: false }
console.log(iterator.next());  // Object { value: "d", done: false }
console.log(iterator.next());  // Object { value: "c", done: false }
console.log(iterator.next());  // Object { value: undefined, done: true } 

迭代器的原理

普通的对象没有实现迭代器接口,不能通过迭代器遍历

let myIterable = {
	a: 1,
	b: 2,
	c: 3
}

// 使用 for..of 循环
for(let elem of myIterable) { 
  console.log(elem);
}

运行结果图:
在这里插入图片描述
那如何实现迭代器(Iterator)接口呢,只需要在这个对象中实[Symbol.iterator]属性方法,[Symbol.iterator]这个属性名看起来怪怪的,Symbol是ES6表示唯一性的标识符,是Symbol的静态属性,标准委员会是为了避免命名的冲突,所以才这样命名。下面我们来实现[Symbol.iterator]属性。如下:

let myIterable = {
    a: 1,
    b: 2,
    c: 3
}
myIterable[Symbol.iterator] = function() {
  let self = this;
  let arr = Object.keys(self);
  let index = 0;
  return {
    next() {
      return index < arr.length ? {value: self[arr[index++]], done: false} : {value: undefined, done: true};
    }
  }
}
for(const i of myIterable) {
  console.log(i);
}

将myIterable对象添加Symbol.iterator属性,同时在返回的next方法中,添加两个属性,既让它成为了一个可迭代对象。(其实如果真的有这样的需求,可以考虑使用Map)。

迭代器的实例

原生的具备 Iterator 接口的数据结构如下:
Array
Map
Set
String
TypedArray(类数组)
函数的 arguments 对象
DOM NodeList 对象

Array

Array的原型链上面实现了迭代器接口,如下:

Array.prototype.values === Array.prototype[Symbol.iterator] 

数组用for…of遍历

const array1 = ['a', 'b', 'c'];
const iterator = array1.values();
//下面两个for of后面接数组对象或数组的迭代器对象都能遍历数组
for (const value of iterator) {
  console.log(value);
}//输出a b c
for (const value of array1 ) {
  console.log(value);
}//输出a b c
//有朋友会问,为什么上面的iterator 也能用for...of遍历
//在控制台执行两条语句
//const array1 = ['a', 'b', 'c']; 
//array1.values()[Symbol.iterator];
//执行结果:ƒ [Symbol.iterator]() { [native code] } 为一个迭代器接口

用iterator 遍历数组:

const array1 = ['a', 'b', 'c'];
const iterator = array1.values();
console.log(iterator.next());  // Object { value: "a", done: false }
console.log(iterator.next());  // Object { value: "d", done: false }
console.log(iterator.next());  // Object { value: "c", done: false }
console.log(iterator.next());  // Object { value: undefined, done: true } 

运行结果图:
在这里插入图片描述
数组可以自己实现迭代器接口(Symbol.iterator为Symbol的静态属性):

const array1 = ['a', 'b', 'c'];
Array.prototype[Symbol.iterator] = function(){
	var self = this;
    let index = -1;
    return {
        next: function() {
            let len = self.length;
            index++;
            //注意这个特殊打印标记,区分是调用自定义的还是原生的
			console.log("自定义的迭代器接口");
            return  index < len ? {value:self[index],done:false}:{value:self[index],done:true};
        }
    };
};
for (let item of array1) {
  console.log(item); // 'a', 'b', 'c'
}

运行结果图:
在这里插入图片描述

Map

Map.prototype[Symbol.iterator]()方法返回Map对象的键值对遍历器。其内部的实现原理和返回值和Map.prototype.entries()一致。

Map.prototype.entries === Map.prototype[Symbol.iterator]

Map的扩展知识:Map构造函数及其属性方法

迭代器遍历

var map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
map.set('d', 4);
map.set('e', 5);

var iter = map[Symbol.iterator]();
iter.next().value; // ['a', 1]
iter.next().value; // ['b', 2]
iter.next().value; // ['c', 3]
iter.next().value; // ['d', 4]
iter.next().value; // ['e', 5]
iter.next();       // undefined

Map的for…of遍历

var map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
map.set('d', 4);
map.set('e', 5);

for (let elem of map) {
  console.log(elem);
  console.log(elem instanceof Array);
}

运行结果图:
在这里插入图片描述
有兴趣的朋友也可以自己定义Map的迭代器接口

Set

Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。

Set.prototype.values === Set.prototype[Symbol.iterator]

迭代器遍历

const set1 = new Set();

set1.add(42);
set1.add('forty two');

const iterator1 = set1[Symbol.iterator]();

console.log(iterator1.next().value);
// expected output: 42

console.log(iterator1.next().value);
// expected output: "forty two"

for…of遍历

const set1 = new Set();

set1.add(42);
set1.add('forty two');

for (let elem of set1) {
  console.log(elem);
}

String

strSymbol.iterator方法返回一个新的Iterator对象,它遍历字符串的代码点,返回每一个代码点的字符串值。

const str = 'The quick red fox jumped over the lazy dog\'s back.';

const iterator = str[Symbol.iterator]();
let theChar = iterator.next();

while (!theChar.done && theChar.value !== ' ') {
  console.log(theChar.value);
  theChar = iterator.next();
  // expected output: "T"
  //                  "h"
  //                  "e"
}

TypedArray

默认的迭代器接口遍历

//Uint8Array 数组类型表示一个8位无符号整型数组,创建时内容被初始化为0。
//创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素。
var arr = new Uint8Array([10, 20, 30, 40, 50]);
for (let n of arr) {
  console.log(n);
}
//输出10 20 30 40. 50
//注意:arr [Symbol.iterator] === Array.prototype.values === Array.prototype[Symbol.iterator]

自定义类数组的迭代器接口进行遍历

let iterable = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // 'a', 'b', 'c'
}
//运行结果输出:a b c

下面是把类数组转换为了数组后,进行遍历

let tyepArray = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};
for (let item of Array.from(tyepArray)) {
  console.log(item); // 'a', 'b', 'c'
}
//运行结果输出a b c

arguments

argumentsSymbol.iterator返回一个迭代器遍历每一个参数的值

function f() {
  // your browser must support for..of loop
  // and let-scoped variables in for loops
  for (let letter of arguments) {
    console.log(letter);
  }
}
f('w', 'y', 'k', 'o', 'p');

DOM NodeList

Node.childNodes 返回包含指定节点的子节点的集合,子节点集合为 NodeList 类型。

for…of遍历

var node = document.createElement("div"); 
var kid1 = document.createElement("p"); 
var kid2 = document.createTextNode("hey"); 
var kid3 = document.createElement("span"); 
node.appendChild(kid1); 
node.appendChild(kid2); 
node.appendChild(kid3); 

var list = node.childNodes;

// 使用 for..of 循环
for(var elem of list) { 
  console.log(elem);
}

迭代器的 return(),throw()

遍历器对象除了具有next方法,还可以具有return方法和throw方法。如果你自己写遍历器对象生成函数,那么next方法是必须部署的,return方法和throw方法是否部署是可选的

return方法的使用场合是,如果for…of循环提前退出(通常是因为出错,或者有break语句),就会调用return方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法。

let myIterable = {
    a: 1,
	b: 2,
	c: 3
}
myIterable[Symbol.iterator] = function() {
  let self = this;
  let arr = Object.keys(self);
  let index = 0;
  return {
	next: function() {
	  return index < arr.length ? {value: self[arr[index++]], done: false} : {value: undefined, done: true};
	},
	return: function() {
		console.log("遍历结束");
		return {value: undefined, done: true};
	}
  }
}

for(const i of myIterable) {
  console.log(i);
  if (i == "2") {
	break;
  }
}
for(const i of myIterable) {
  console.log(i);
  if (i == "2") {
	throw new Error();
  }
}

运行结果图:
在这里插入图片描述
return和throw都会触发迭代器接口中的return方法,throw是先执行return方法后,再抛出错误。

for…of和for …in的区别

1 遍历数组的区别
for…in遍历数组


var arr = ['a', 'b', 'c']; 
for(var i in arr){
   console.log(arr[i]);
 }

for…of遍历数组

var arr = ['a', 'b', 'c']; 
for(var ch of arr){
    console.log(ch);
}

for…of能直接获取数组的元素,for…in能获取到数组下标,再通过下标获取数组元素。

2 遍历对象的区别
for…of不能直接遍历普通对象,需要自定义对象的迭代器接口后才能遍历对象。
for…in能直接遍历对象的key,value

or(let key in obj) {
   console.log('for in key', key)
 }
  1. for…in 循环不仅遍历数字键名,还会遍历手动添加的其它键,甚至包括原型链上的键。
let arr = [1, 2, 3]
arr.set = 'world'  // 手动添加的键
Array.prototype.name = 'hello'  // 原型链上的键
 
for(let item in arr) {
  console.log('item', item)
}
 
/*
  item 0
  item 1
  item 2
  item set
  item name
*/

上一篇:ES6学习第四篇–Let和Var的相同点和区别
下一篇:ES6学习第六篇–生成器(Generator)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值