咱们暂且以数组为例,javascript提供了多种遍历数组的方法,最开始咱们可能习惯使用for循环:javascript
for (var index = 0; index < myArray.length; index++){ console.log(myArray[index]); }
这种写法比较麻烦,因而数组内提供了forEach方法:java
myArray.forEach(function (value) { console.log(value); });
这段代码看起来更加简洁,但这种方法也有一个小缺陷:你不能使用break语句中断循环,也不能使用return语句返回到外层函数。
for...in循环(其实for...in循环是为了遍历对象而设计的,并不适合遍历数组,切记!切记!切记!重要的事情说三遍!!!)es6
for (var index in myArray) { // 千万别这样作 console.log(myArray[index]); }
for...in循环便利数组绝对是一个糟糕的选择,缘由以下:数组
在这段代码中,赋给index的值不是实际的数字,而是字符串“0”、“1”、“2”,此时极可能在无心之间进行字符串算数计算,例如:“2” + 1 == “21”,这给编码过程带来极大的不便。数据结构
做用于数组的for-in循环体除了遍历数组元素外,还会遍历自定义属性。举个例子,若是你的数组中有一个可枚举属性myArray.name,循环将额外执行一次,遍历到名为“name”的索引。就连数组原型链上的属性都能被访问到。ide
最让人震惊的是,在某些状况下,这段代码可能按照随机顺序遍历数组元素。函数
简而言之,for-in是为普通对象设计的,你能够遍历获得字符串类型的键,所以不适用于数组遍历。this
强大的for-of循环编码
for (var value of myArray) { console.log(value); }
优势:spa
其实不只能够便利数组,ES6新增长的数据结构set和map也可使用for-of循环来进行遍历
var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]); for (var e of engines) { console.log(e); } // Gecko // Trident // Webkit var es6 = new Map(); es6.set("edition", 6); es6.set("committee", "TC39"); es6.set("standard", "ECMA-262"); for (var [name, value] of es6) { console.log(name + ": " + value); } // edition: 6 // committee: TC39 // standard: ECMA-262
上面代码演示了如何遍历 Set 结构和 Map 结构。值得注意的地方有两个,首先,遍历的顺序是按照各个成员被添加进数据结构的顺序。其次,Set 结构遍历时,返回的是一个值,而 Map 结构遍历时,返回的是一个数组,该数组的两个成员分别为当前 Map 成员的键名和键值。
甚至使用for-of循环亦能够遍历字符串:
for (var chr of "") { alert(chr); }
遍历器(Iterator)就是这样一种机制。它是一种接口,为各类不一样的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就能够完成遍历操做(即依次处理该数据结构的全部成员)。
Iterator的做用有三个:一是为各类数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员可以按某种次序排列;三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。
Iterator的遍历过程是这样的。
(1)建立一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,能够将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来讲,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
在ES6中,有些数据结构原生具有Iterator接口(好比数组),即不用任何处理,就能够被for...of循环遍历,有些就不行(好比对象)。缘由在于,这些数据结构原生部署了Symbol.iterator属性,另一些数据结构没有。凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
在ES6中,有三类数据结构原生具有Iterator接口:数组、某些相似数组的对象、Set和Map结构。对于这三类数据结构,不用本身写遍历器生成函数,for...of循环会自动遍历它们。除此以外,其余数据结构(主要是对象)的Iterator接口,都须要本身在Symbol.iterator属性上面部署,这样才会被for...of循环遍历。
有些数据结构是在现有数据结构的基础上,计算生成的。好比,ES6的数组、Set、Map 都部署了如下三个方法,调用后都返回遍历器对象。
这三个方法调用后生成的遍历器对象,所遍历的都是计算生成的数据结构。
屏幕快照 2017-06-07 下午3.46.23.png
值得注意的是ES6中的新数据结构set和map原生提供的除了这三个遍历器生成函数以外,还有一个遍历方法forEach()。切记不要将遍历器生成函数和遍历方法混为一谈
let map = new Map([ ['f','no'], ['t','true'] ]); for (let[key,value] of map.entries()){ console.log(key,value); } //等同于使用map.entries() for (let[key,value] of map){ console.log(key,value); }
前面咱们提到过那些没有部署过Symbol.iterator属性的数据结构也便是不具有Iterator接口的的对象是不能使用for...of方法遍历的。经过Gnenrator函数为这类对象加上这个Iterator接口就能够了。
function* objectEntries(obj) { let propKeys = Reflect.ownKeys(obj); for (let propKey of propKeys) { yield [propKey, obj[propKey]]; } } let jane = { first: 'Jane', last: 'Doe' }; for (let [key, value] of objectEntries(jane)) { console.log(`${key}: ${value}`); } // first: Jane // last: Doe
上面代码中,对象jane原生不具有 Iterator 接口,没法用for...of遍历。这时,咱们经过 Generator 函数objectEntries为它加上遍历器接口,就能够用for...of遍历了。加上遍历器接口的另外一种写法是,将 Generator 函数加到对象的Symbol.iterator属性上面。
function* objectEntries() { let propKeys = Object.keys(this); for (let propKey of propKeys) { yield [propKey, this[propKey]]; } } let jane = { first: 'Jane', last: 'Doe' }; jane[Symbol.iterator] = objectEntries; for (let [key, value] of jane) { console.log(`${key}: ${value}`); } // first: Jane // last: Doe
除了for...of循环之外,扩展运算符(...)、解构赋值和Array.from方法内部调用的,都是遍历器接口。这意味着,它们均可以将 Generator 函数返回的 Iterator 对象,做为参数。
function* numbers () { yield 1 yield 2 return 3 yield 4 } // 扩展运算符 [...numbers()] // [1, 2] // Array.from 方法 Array.from(numbers()) // [1, 2] // 解构赋值 let [x, y] = numbers(); x // 1 y // 2 // for...of 循环 for (let n of numbers()) { console.log(n) } // 1 // 2
总而言之,ES6提供一个强大的遍历方法for-of,对于那些原生具有Iterator接口的数据结构,能够直接使用,而不具有具有Iterator接口的对象,须要部署Symbol.iterator属性才能使用。
做者:大闯仔 连接:https://www.jianshu.com/p/b9e31305bcbb 來源:简书 著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。