1.iterator(遍历器)的概念
-
javascript 原有的表示“集合”的数据结构主要是数组(Array)和对象(Object),ES6新增了Set和Map数据结构,用户可以组合使用它们,因此就需要一种统一的接口机制来处理所有不同的数据结构。
-
iterator就产生,它是一种接口,为各种不同的数据结构提供统一的访问机制,然和数据结构只要部署iterator接口,就可以完成遍历操作。
-
iterator的作用有三个
1.为各种数据结构提供一个统一的,简单的访问接口
2.使得数据结构的成员可以按某种次序排列
3.ES6穿凿了一个全新的遍历命令 - for – of 循环 -
iterator的遍历过程如下
1.创建一个只针对想,指向当前数据结构的起始位置。也就是说,遍历器对象本质上就是一个只针对象。
2.第一此调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
3.第二次调用指针对象的next方法,指针就只想数据结构的第二个成员。
4.不断地调用只针对想next方法,知道他只想数据结构的结束位置。 -
每次调用next方法,都会返回数据结构当前的成员信息。返回一个value和done两个属性的对象。
其中value为当前成员的值,done为一个布尔值,表示遍历是否结束。
2.模拟next返回值的方法
{
//模拟next方法返回值的例子
let it = makeIterator(['a', 'b']);
it[Symbol.isConcatSpreadable] = true; //让对象展开,方便我们再控制台查看。
function makeIterator(array) {
let nextIndex = 0;
return {
next() {
return nextIndex < array.length ? {
value: array[nextIndex++],
done: false
} : {
value: undefined,
done: true
}
//三目运算符,根据下标返回相应的数据。
}
}
}
console.log(it.next()); //{value: "a", done: false}
console.log(it.next()); //{value: "b", done: false}
console.log(it.next()); //{value: undefined, done: true}
}
3.默认的iterator接口
-
iterator接口的母的时为了所有数据结构提供一种统一的访问机制,即for…of…循环,当使用for…of循环遍历某种数据结构的时候,该循环会自动去寻找iteratir接口。
-
数据结构只要部署了iterator接口就是可遍历的,
-
一个数据结构只要具备Symbol.iterator属性就可以被认为时可遍历的。
[Symbol.iterator]属性
{
let obj = {
[Symbol.iterator]() {
return {
next() {
return {
value: 1,
done: true
}
}
}
}
}
}
上述的对象obj因为具有Symbol.iterator属性,执行这个属性会返回一个遍历器对象
该对象的根本特征就是具有next方法,每次调用next方法都会返回一个当前成员的信息对象,具有value和done两个属性。
- ES6的有些数据结构是具有iterator接口,不用处理就可以用for…of循环。
Array,Map,Set,String,函数arguments对象,NodeList对象。
ES6的有些数据结构是具有iterator接口,不用处理就可以用for...of循环。
数组的Symbol.iterator属性
{
let arr = ['a','b','c'];
let iter = arr[Symbol.iterator]();
//调用对象里面的next()
console.log(iter.next()); //{value: "a", done: false}
console.log(iter.next()); //{value: "b", done: false}
console.log(iter.next()); //{value: "c", done: false}
// 我们也可以直接用for...of循环遍历arr数组
for(let i of arr){
console.log(i)
//a b c
}
}
上面的代码中,变量arr是一个数组,器原生具有遍历器接口,部署在arr的Symbol.iterator属性上,所以调用
这个属性就会得到遍历器对象。
为对象添加iterator接口(例子)
{
let testObj = {
data:['hello','world'],
name:'kjh',
[Symbol.iterator](){
let self = this;
let index = 0;
return {
next(){
if(index < self.data.length){
return {
value:self.data[index++],
done:false
};
}
else{
return {
value:undefined,done:true
}
}
}
}
}
};
let iter = testObj[Symbol.iterator]();
console.log(iter.next());//{value: "hello", done: false}
console.log(iter.next());//{value: "world", done: false}
console.log(iter.next());//{value: undefined, done: true}
}
类数组对象调用数组的Symbol.iterator就可以实现for...of遍历
{
let arrLike = {
'0':1,
'1':2,
length:2,
[Symbol.iterator]:Array.prototype[Symbol.iterator]
};
for(let item of arrLike){
console.log(item);
//1
//2
}
}
普通对象调用数组的方法并无效果(控制台都没有输出结果)
{
let arrLike = {
'0':1,
'1':2,
[Symbol.iterator]:Array.prototype[Symbol.iterator]
};
for(let item of arrLike){
console.log(item);
}
}
这是因为类似数组的对象存在(数值键名和length属性),部署iterator接口可以直接使用数组的iterator方法。
4.调用Iterator接口的场合
结构赋值
对数组和Set结构进行解构赋值的时候,会默认调用Symbol.iterator方法。
{
let set = new Set().add('a').add('b').add('c');
let [x,y] = set;
console.log(x,y); //x = 'a' y = 'b'
let [first,...rest] = set;
console.log(first,rest);
//a (2) ["b", "c"]
}
扩展运算符
扩展运算符(...)也会默认使用默认的Iterator接口
{
//例1:
var str = 'hello';
console.log([...str]);
//(5) ["h", "e", "l", "l", "o"]
//例2:
let arr = ['b','c'];
console.log(['a',...arr,'d']);
//(4) ["a", "b", "c", "d"]
}
其他场合
yield for...of() Array.from() Map() Set() weakMap() weakSet() Promise.all() Promise.race()
5. for … of 循环
- 一个数据结构只要部署了Symbol.iterator属性,就被视为具有Iterator接口,就可以用for…of循环遍历他的成员。
- 也就是说,for…of循环内部调用的时数据结构的Symbol.iterator方法。
- for… of…循环可以使用的范围包括数组,Set和Map结构,某些类似数组的对象。
6.字符串的Iterator接口
字符串是一个类似数组的对象,也具有原生Iterator接口
{
let someString = 'hi';
console.log(typeof someString[Symbol.iterator]);
//function
let iter = someString[Symbol.iterator]();
console.log(iter.next()); //{value: "h", done: false}
console.log(iter.next()); //{value: "i", done: false}
console.log(iter.next()); //{value: undefined, done: true}
}
//上面的代码中,调用Symbol.iterator方法返回一个遍历器对象,在上面可以调用next方法实现对于字符串的遍历。
var str = new String('hi');
console.log([...str]);
//(2) ["h", "i"]
iterator接口与Gennerator函数
遍历器对象的return(),throw()
(学完generator函数再回顾)
7. 数组
{
//数组原生具备iterator接口(默认部署了Symbol.iterator属性)
const arr = ['red','green','blue'];
for(let v of arr){
console.log(v); //red green blue
}
let obj = {};
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);
for(let k of obj){
console.log(k); //red green blue
}
}
//空对象obj部署了数组arr的Symbol.iterator属性,结果Obj的for...of循环产生了与arr完全一样的结果。
{
//for... in... 和 for...of...
let arr = ['a','b','c','d'];
arr.foo = 'hello';
//for...in 只能循环获取对象的键名
for(let a in arr){
console.log(a); //0 1 2 3 foo
}
//for...of 只能换取键值
for(let b of arr){
console.log(b); //a b c d
}
// for ... of 循环不会返回数组arr的foo属性。
}
8.Set和Map
{
Set和Map结构具有Iterator接口,可以直接使用for...of循环
let set = new Set(['name','age','qq']);
for(let i of set){
console.log(i);
//name
//age
//qq
}
set结构遍历返回的是一个值
let map = new Map();
map
.set("name",'kjh')
.set('age',18)
.set('qq','861918672')
for(let k of map){
console.log(k);
//(2) ["name", "kjh"]
//(2)["age", 18]
//(2) ["qq", "861918672"]
}
//map结构遍历返回的是一个数组,成员为map成员的键名和键值。
}
9.类似数组的对象 (字符串,DOM NodeList对象,arguments对象)
{
let str = 'hello';
for(let i of str){
console.log(i)
// h e l l o
}
//arguments
function printArgs(){
for(let i of arguments){
console.log(i)
}
}
printArgs('a','b');
//a b
//如上说过不具有数字键名和length属性的普通类数组对象不能使用数组的遍历接口
//一个方法就是使用Array.from 将其转换为数组
let arrLike = {
'0':'kjh',
'1':18,
length:2
}
for(let i of Array.from(arrLike)){
console.log(i)
//kjh 18
}
}
10.对象
//对于普通的对象,for...of不能直接使用,必须部署iterator接口,但是这样的情况下,for..in依旧可以使用
{
let obj = {
name:'kjh',
age:18,
qq:'861923'
}
for(let i in obj){
console.log(i);
//name age qq
}
for(let i of obj){
console.log(i);
//obj is not iterable
}
//解决方法,给对象部署iterator接口
//对象提供了Object.keys() Object.values() Object.entries() 方法
for(let i of Object.keys(obj)){
console.log(i);
//name
//age
//qq
}
for(let i of Object.values(obj)){
console.log(i);
//kjh
//18
//861923
}
for(let i of Object.entries(obj)){
console.log(i);
//(2) ["name", "kjh"]
//(2) ["age", 18]
//(2) ["qq", "861923"]
}
}
11.计算生成的数据结构
有些数据结构时再现有数据结构的基础上计算生成的,比如ES6的数组,set和Map都部署了一下3个方法
+ entries() 返回一个遍历器对象,遍历键名键值
+ keys() 返回一个遍历器对象,遍历键名
+ values() 返回一个遍历器对象,遍历所有的键值
{
// 举个例子
let arr = ['a','b','c'];
for(let i of arr.keys()){
console.log(i) //0 1 2
}
for(let i of arr.values()){
console.log(i) //a b c
}
for(let i of arr.entries()){
console.log(i);
//(2) [0, "a"]
//(2) [1, "b"]
//(2) [2, "c"]
}
}
12. for…in 和 for…of 的优缺点
1.for...in 不适合遍历数组,因为for...in循环以字符串作为键名
2.for...in 循环会拿到原型上的键名
for...of
1.for...of 有for .. in 的优点,没有他的缺点
2.可以配合break continue return使用