循环、遍历、迭代 知识储备

570 阅读3分钟

循环、遍历、迭代

循环:语言层面上的语法 -> 重复执行一段程序的方案

遍历:业务层面上的做法 -> 观察或者获取集合中的元素的一种做法

迭代:市县层面上的概念 -> 实现遍历的底层方案其实就是迭代

ECMAScript3 -> 没有针对可迭代对象的具体的遍历方法

ECMAScript5 -> 7个专门针对数组的遍历方法:forEach、reduce、some、every、reduceRight、map、filter

ECMAScript5 -> for in 对象的遍历方法

for in 主要是针对对象进行遍历的,比如:objectarray,typeof [] 为 object,一种特殊的对象,不能对 new Map()new Set() 进行遍历。

可以看 MDN 的描述:

  1. for...in 语句以任意顺序遍历一个对象的除 Symbol 以外的可枚举属性。
  2. for...in 不应该用于迭代一个关注索引顺序的 Array。
  3. 如果你只要考虑对象本身的属性,而不是它的原型,那么使用 hasOwnProperty() 来确定某属性是否是对象本身的属性

除了 for in 还有一个类似的 循环方法 for of,它主要用于在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环。

for of 是依赖于迭代器,而这个迭代器就是 Symbol.iterator,那哪一些类型具备这个迭代器能够使用 for of 呢?

Array.prototype[@@iterator]() // 包括 arguments
TypedArray.prototype[@@iterator]()
String.prototype[@@iterator]()
Map.prototype[@@iterator]()
Set.prototype[@@iterator]()

注意:Object 不具备 Symbol.iterator 迭代器

Symbol.iterator 的属性特性:

  1. writable: false
  2. configurable: false
  3. enumerable: false

for of 和 for in 的差别

for in:

  1. 遍历是无序的
  2. 针对于 Object,Array 也可用,Map 和 Set 不可用
  3. 遍历可枚举的属性,包含原型上的属性,可以使用 hasOwnProperty 处理自身属性
  4. 遍历得到的 item 是键名

for of:

  1. 遍历是有序的
  2. 针对于存在迭代器(Symbol.iterator)的类型,比如:Array、TypedArray、String、Map、Set
  3. 不可遍历与 Object,因为它没有迭代器
  4. 遍历得到的 item 是键值

自定义迭代器

// 自定义迭代器,Symbol.iterator
// 对于 Object 是没有内置迭代器 Symbol.iterator 的,所以无法使用 for of 去迭代,因为 for of 依赖于迭代器 Symbol.iterator

// 先看一下遍历迭代器的流程是怎么样的

// ----------- 迭代器流程 Start -----------

const iterator = function* generator(arr) {
  let index = 0;
  while (index < arr.length) {
    yield arr[index++];   
  }
}

// 开始迭代
const _iterator = iterator([1, 2, 3]); // 执行迭代器,得到 generator 对象,包含 next 方法

console.log(_iterator.next()); // {value: 1, done: false}
console.log(_iterator.next()); // {value: 2, done: false}
console.log(_iterator.next()); // {value: 3, done: false}
console.log(_iterator.next()); // {value: undefined, done: true} // 判断为 done 为 true,不在执行

// 以上就是一个迭代过程,像 for of 迭代的时候,内部就是调用 next,如果 done 为 true 就不在输出 value,并且停止调用 next

// ----------- 迭代器流程 End -----------

// ES6 方式自定义迭代器
Object.prototype[Symbol.iterator] = function* () {
  let index = 0;
  while (index < this.length) {
    yield this[index++];   
  }
};

// ES6以前方式自定义迭代器
Object.prototype[Symbol.iterator] = function () {
  let index = 0;
  const _this = this;
  return {
    next() {
      return index < _this.length ? { value: _this[index++], done: false } : {value: undefined, done: true}
    }
  }
};

const obj = {
  0: 1,
  1: 2,
  2: 3,
  length: 3
}

for (let val of obj) {
  console.log(val);
}

// 输出: 1 2 3

// for of 的内部就是先调用 Object.prototype[Symbol.iterator] 得到迭代器对象,
// 然后根据 done 是否为 true,如果不是则不断去 next,知道 done 为 true 则停止迭代

// 这里有一点,使用 generator 的时候为什么 while 的条件不直接为 while(true){} 不断循环让迭代去 next 内,
// 首先要知道,迭代的时候是根据返回的 done 为 true 才停止的,那对于 generator 函数,
// 什么时候才返回 done 为 true 呢,那就是没有 yield 预等待输出的时候,如果设置为 while(true){},
// 那么 done 永远都为 false,for of 迭代的时候,将不会停止。