遍历器Iterator就是一种机制,是一种接口,能够为不一样的数据结构提供统一的访问机制
for...of循环
,Iterator接口主要就是供for...of循环使用的!
返回的是一个对象,对象有两个属性,value和done两个属性。value是当前成员的值,done是一个布尔值,表示遍历是否结束
另外done:false和vallue:undefined是能够省略的,由于不返回done属性那么确定就是undefined被判断为false,不返回value属性那么确定就是undefined
须要注意的是,在原生的iterator接口实现中,对象是不能够实现iterator接口的,也就是for...of循环会失败
// 1. 对于对象来讲,只能遍历key为索引的键值 function makeIterator(obj){ var index=0; return { next:function(){ return { done:obj[index]?true:false, value:obj[index++] } } } } var obj={0:'w',1:444,5:555,a:'yy'} var res1=makeIterator(obj) console.log(res1.next());//{done: true, value: "w"} console.log(res1.next());//{done: true, value: 444} console.log(res1.next());//{done: false, value: undefined} console.log(res1.next());//{done: false, value: undefined} // 而且注意:索引为数字也不必定能遍历到,由于要根据index的值,若是要遍历到5,那么须要继续调用两次next // 1.2 可是不重写iterator接口(如上使用函数,或者重写Symbol.iterator接口)的话 // 对 对象使用for..of循环会报错! for(var item of {0:1,1:22}){ console.log(item);//Uncaught TypeError: {(intermediate value)(intermediate value)} is not iterable } // 2. 对于数组来讲 function makeArray(arr){ var index=0; return { next:function(){ return { done:arr[index]?true:false, value:arr[index++] } } } } var res2=makeArray([5,4,3]) console.log(res2.next())//{done: true, value: 5} console.log(res2.next())//{done: true, value: 4} console.log(res2.next())//{done: true, value: 3} console.log(res2.next())//{done: false, value: undefined} 复制代码
Iterator接口的目的即便为全部的数据结构,提供统一的访问机制,即for...of循环
可遍历的iterable
Symbol.iterator属性上
,因此咱们能够经过判断是否具备Symbol.iterator属性做为可遍历的依据
通常获取Symbol.iterator属性都是使用[Symbol.iterator]的形式,由于Symbol.iterator至关于一个表示式,相似[1+'2']使用[]形式
// 1. 数组 var arr=[] console.log(arr[(Symbol.iterator)])//ƒ values() { [native code] } // 2. Map var map=new Map() console.log(map[Symbol.iterator]);//ƒ entries() { [native code] } // 3. Set var set=new Set() console.log(set[Symbol.iterator]);//ƒ values() { [native code] } // 4. 对象(没有iterator接口) var obj={} console.log(obj[Symbol.iterator]);//undeined // 5. 对象添加数字属性仍是没有terator接口的,除非直接修改对象的Symbol.iterator接口 var obj2={0:1,1:222} console.log(obj2[Symbol.iterator]);//undeined // 6. 字符串也有iterator接口 var str="hello world" console.log(str[Symbol.iterator]);//ƒ [Symbol.iterator]() { [native code] } // 7. 数字没有 var nums=1234 console.log(nums[Symbol.iterator]);//undeined // 8. arguments,函数参数数组有! function func(){ console.log(arguments[Symbol.iterator]);//ƒ values() { [native code] } } func(1,4,8) // 9. Nodeslist 节点列表有! console.log(document.getElementsByClassName("one")[Symbol.iterator]);//ƒ values() { [native code] } 复制代码
Array,String,Nodelist,arguments,Map,Set
for...of循环会自动遍历他们
在Symbol.iterator属性上部署,这样才会被for...of循环遍历到
var arr=[4,6,8] // 1. for...of for(var item of arr){ console.log(item); } // 2. 调用Symbol.iterator接口方法 var res=arr[Symbol.iterator]() console.log(res.next())//{value: 4, done: false} console.log(res.next())//{value: 6, done: false} console.log(res.next());//{value: 8, done: false} console.log(res.next());//{value: undefined, done: true} 复制代码
对象没有部署iterator接口的缘由:对象的哪一个属性先遍历,哪一个属性后遍历是不肯定的。
class RangeIterator { constructor(start, stop) { this.value = start; this.stop = stop; } [Symbol.iterator]() { console.log("调用Symbol.iterator属性") return this; } next() { console.log("调用next属性") var value = this.value; if (value < this.stop) { this.value++; return {done: false, value: value}; } return {done: true, value: undefined}; } } function range(start, stop) { return new RangeIterator(start, stop); } // 获得一个对象 console.log(range(0, 3));//RangeIterator {value: 0, stop: 3} // 而后for...of遍历该对象就至关于一直调用该对象的next方法,直到done为true console.log(range(0, 3).__proto__); /* 该实例的原型具备如下属性 constructor: class RangeIterator next: ƒ next() Symbol(Symbol.iterator): ƒ [Symbol.iterator]() __proto__: Object */ for (var value of range(0, 3)) { console.log(value); // 0, 1, 2 } 复制代码
咱们实际上给对象部署ierator接口能够直接调用数组的iterator属性
并且必须加上length属性才能在for...of循环获取到值
类数组对象指的就是具备length属性的对象,因此length属性必须有,决定着循环的次数
给对象的Symbol.iterator属性设置为数组的Symbol.iterator属性。[Symbol.iterator]:Array.prototype[Symbol.iterator]
// 1. 对象具备0,1,2这些数字属性 var obj1={ 0:'a', 1:3333, 2:'w', length:3, [Symbol.iterator]:Array.prototype[Symbol.iterator] } for(var item of obj1){ console.log(item);//a,3333,w } // 2. 没有数字属性 var obj2={ length:3, [Symbol.iterator]:Array.prototype[Symbol.iterator] } for(var item of obj2){ console.log(item);//undefined,undefined,undefined } // 3. 没有length属性(此时啥都没打印。) var obj3={ [Symbol.iterator]:Array.prototype[Symbol.iterator] } for(var item of obj3){ console.log(item); } // 4. 具备非数字属性,且无length属性(不打印。) var obj4={ a:'a', [Symbol.iterator]:Array.prototype[Symbol.iterator] } for(var item of obj4){ console.log(item); } // 5. 具备非数字和length属性 // 会执行length所记录的长度次数,可是没有对应索引属性,只会获得unefined var obj5={ a:'a', length:1, [Symbol.iterator]:Array.prototype[Symbol.iterator] } for(var item of obj5){ console.log(item);// undefined } 复制代码
var set=new Set().add('a').add('b').add('c') let [x,y]=set; console.log(x,y);//a,b let [a,...b]=set; console.log(a,b);//a,['b','c'] 复制代码
var str="hello" console.log([...str]);//["h", "e", "l", "l", "o"] var arr=['a',2,6] console.log([...arr]);//["a", 2, 6] 复制代码
var gen=function *(){ yield 1; yield* [2,3,4]; yield 9 } var res=gen(); console.log(res.next())//{value: 1, done: false} console.log(res.next())//{value: 2, done: false} console.log(res.next())//{value: 3, done: false} console.log(res.next())//{value: 4, done: false} console.log(res.next());//{value: 9, done: false} console.log(res.next());//{value: undefined, done: true} 复制代码
也有length属性
,也原生具备iterator接口
var str="hi" //Symbol.iterator属性是一个函数返回一个遍历器对象 console.log(typeof str[Symbol.iterator]);//function var res=str[Symbol.iterator](); console.log(res.next());//{value: "h", done: false} console.log(res.next());//{value: "i", done: false} console.log(res.next());//{value: undefined, done: true} 复制代码
// var str="hello" var str=new String("hello") console.log([...str]);//["h", "e", "l", "l", "o"] str[Symbol.iterator]=function *(){ yield 1; yield 2; yield 3; } console.log([...str]);//[1, 2, 3] 复制代码
最简实现:和generator函数结合
// 形式1 var res1={ [Symbol.iterator]:function *(){ yield 1; yield 11; yield 111; } } console.log([...res1]);//[1, 11, 111] // 形式2 var res2={ *[Symbol.iterator](){ yield 2; yield 22; yield 222; } } console.log([...res2]);//[2, 22, 222] 复制代码
return方法和throw方法
自定义Symbol.iterator方法,那么return()和throw()可选
for...of循环提早退出的话就会调用return方法
// return 方法 var arr=[2,4,6] arr[Symbol.iterator]=function(){ return { next(){ return {value:'a',done:false} }, return(){ return {done:true} } } } var i=0; for(var item of arr){ i++; console.log(item);//a,a,a,a if(i>3){ // 调用return方法的方式一:break // break; // 2.抛出错误 throw new Error("err");//Error: err } } 复制代码
for...of循环内部调用的就是变量自己的Symbol.iterator方法
而且for...of调用的是遍历器接口(只会返回具备数字索引的属性),而for..in不是调用遍历器接口的,因此能够获得非数字索引的属性
var arr=[1,2,5] arr.foo="ffff" // for...of 循环不能获得非数字索引的属性 for(var item of arr){ console.log(item) // 1,2,5 } // for ...in for(var i in arr){ console.log(arr[i]) // 能够获得给数字索引的属性 //1,2,5,ffff } 复制代码
var arr=['a',2,7] for(var item of arr){ console.log(item)//a,2,7 } var obj={} // 把数组的Symbol.iterator接口设置到对象中 // 注意要使用bind绑定,而不是调用该方法 obj[Symbol.iterator]=arr[Symbol.iterator].bind(arr); for(var item of obj){ console.log(item);//a,2,7 } // 给对象添加新的属性 obj['a']='yy' obj[3]=3 // 能够注意到属性没有添加到Symbol.iterator中 console.log(obj);//{3: 3, a: "yy", Symbol(Symbol.iterator): ƒ} // 能够看到BoundThis中没有属性a,3 /* arguments: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.invokeGetter (<anonymous>:1:142)] caller: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.invokeGetter (<anonymous>:1:142)] length: 0 name: "bound values" __proto__: ƒ () [[TargetFunction]]: ƒ values() [[BoundThis]]: Array(3) 0: "a" 1: 2 2: 7 length: 3 __proto__: Array(0) [[BoundArgs]]: Array(0) */ for(var item of obj){ // 没有遍历到后面添加到的几个属性 console.log(item);//a,2,7 } 复制代码
// 1. set var set=new Set() set.add(1) set.add(10) set.add('a') for(var item of set){ console.log(item);//1,10,a } // 2. Map var map=new Map(); map.set('a',1) map.set('ab',11) map.set('ba',1081) for(var item of map){ console.log(item) // ["a", 1],["ab", 11],["ba", 1081] } 复制代码
nodelist,arguments,字符串...
// 1.字符串 var str="hello" for(var item of str){ console.log(item);//h,e,l,l,o } // 2. nodelist对象 var nodes=document.getElementsByClassName('one') for(var item of nodes){ console.log(item);//<div class="one"></div> } // 3. arguments对象 function args(){ for(var item of arguments){ console.log(item);//5,'w',98 } } args(5,'w',98) 复制代码
可是对于对象来讲,即便添加了length属性成为类数组对象
或者使用Array.from方法转换为数组先
var obj={length:2,0:1,1:'e',t:333} // 虽然是类数组对象,可是没有原生的Symbol.iterator方法,也没有重写 // 因此仍是会报错 /* for(var item of obj){ console.log(item);//TypeError obj is not iterable } */ // 解决方法1,设置Symbol.iterator方法 /* obj[Symbol.iterator]=Array.prototype[Symbol.iterator] for(var item of obj){ console.log(item);//1,e } */ // 解决方法2:使用Array.from转换为数组 obj=Array.from(obj) for(var item of obj){ console.log(item);//1,e } 复制代码
1.经过for..in循环间接获取键值,2.经过Object.keys()生成数组再遍历
var obj={a:2,0:'ss',1:111} // 1. for...in循环 for(var i in obj){ console.log(obj[i]);//ss,111,2 } // 2. Object.keys()生成数组 for(var item of Object.keys(obj)){ console.log(obj[item])//ss,111,2 } 复制代码
不可使用break,continue,return实现退出!
var arr=[4,5,77,0,323] arr.forEach((item,i)=>{ console.log(item);//4,5,77,0,323 if(i==2){ // return ;// 退出失败。不报错 // break;//报错,Illegal break statement // continue;// 报错:Illegal continue statement: no surrounding iteration statement } }) 复制代码
本文参考 阮一峰ES6教程 node
本文使用 mdnice 排版es6