ES6之遍历语法

 

如何使用遍历的

咱们暂且以数组为例,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

  • 这是最简洁、最直接的遍历数组元素的语法
  • 这个方法避开了for-in循环的全部缺陷
  • 与forEach()不一样的是,它能够正确响应break、continue和return语句

其实不只能够便利数组,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);
}

深刻理解

1.Iterator(遍历器)的概念

遍历器(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循环遍历。

2.计算生成的数据结构

有些数据结构是在现有数据结构的基础上,计算生成的。好比,ES6的数组、Set、Map 都部署了如下三个方法,调用后都返回遍历器对象。

  • entries() 返回一个遍历器对象,用来遍历[键名, 键值]组成的数组。对于数组,键名就是索引值;对于 Set,键名与键值相同。Map 结构的 Iterator 接口,默认就是调用entries方法。
  • keys() 返回一个遍历器对象,用来遍历全部的键名。
  • values() 返回一个遍历器对象,用来遍历全部的键值。

这三个方法调用后生成的遍历器对象,所遍历的都是计算生成的数据结构。

 

 

屏幕快照 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);
}

3.原生javascript对象的遍历

前面咱们提到过那些没有部署过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 來源:简书 著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。

相关文章
相关标签/搜索