Iterator和 for...of 循环

Iterator 的概念
  • 遍历器(Iterator就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个
一、 是为各种数据结构,提供一个统一的、简便的访问接口;
二 、是使得数据结构的成员能够按某种次序排列;
三 、是 ES6 创造了一种新的遍历命令 for...of循环,Iterator 接口主要供for…of消费。
  • next方法返回值
function upper(array){
    let index = 0
    return{
        next(){
            return index < array.length ? {value:array[index++], done:false}: {value:undefined, done:true}
           // return index < array.length ? {value:array[index++]}: {done:true}
           // 简便的写法(下面会有介绍原因)
        }
    }
}

let maker = new upper([1, 2, 3])
console.log( maker.next())   // value: 1 done: false
console.log( maker.next())   // value: 2 done: false
console.log( maker.next())   // value: 3 done: false
console.log( maker.next())   // value: undefined done: true
console.log( maker.next())   // value: undefined done: true

//每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回
//一个包含`value`和`done`两个属性的对象。其中,`value属性是当前成员的值`,
//`done属性是一个布尔值,表示遍历是否结束`。
  • 指针对象的 next方法,用来移动指针。开始时,指针指向数组的开始位置。然后,每次调用next方法,指针就会指向数组的下一个成员。第一次调用,指向 1 ;第二次调用,指向 2 。
  • next方法返回一个对象,表示当前数据成员的信息。这个对象具有valuedone两个属性,value属性返回当前位置的成员,done属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用next方法。
  • 对于遍历器对象来说,done: falsevalue: undefined属性都是可以省略的,所以我们在代码编写时可以忽略这两个属性的设置

默认的 Iterator接口
  • ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。
  • Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名 Symbol.iterator,它是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内。
// 手动部署遍历器生成方法
// 为对象添加 Iterator 接口
const obj = {
  [Symbol.iterator] : function () {
    return {
      next: function () {
        return {
          value: 1,
          done: true
        };
      }
    };
  }
};
obj[Symbol.iterator]().next()  // {value: 1, done:true}
  • 对于类似数组的对象(存在数值键名和length属性),部署 Iterator接口,有一个简便方法,就是Symbol.iterator方法直接引用数组的 Iterator 接口。
// NodeList 为类数组对象
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
  • 但是对于非类数组对象,上面的方法是无效的。
let iterable = {
  a: 'a',
  b: 'b',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let value of iterable) {
  console.log(value); // undefined, undefined
}

调用 Iterator 接口的场合
(1)解构赋值
对数组和 Set 结构进行解构赋值时,会默认调用 Symbol.iterator方法。
let newarrray = [1, 2, 3, 4]
let [start, ...end] = newarrray

let newset = new Set([1, 2, 2, 3])
let [start1, ...end1] = newset
// 上面的两种方法都会默认调用 Symbol.iterator方法,即遍历 newarray 和 newset
(2)扩展运算符
扩展运算符(…)也会调用默认的 Iterator 接口。
// 例一
var str = 'hello';
[...str] //  ['h','e','l','l','o']

// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']
(3)其他场合

由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。

for...of
Array.from()
Map(), Set(), WeakMap(), WeakSet()
Promise.all()
Promise.race()

上面这些的参数都可以接收数组作为参数。


字符串的 Iterator 接口
  • 下面是一种比较特殊的情况
var str = new String("hi");

str[Symbol.iterator] = function() {
  return {
    next: function() {
      if (this._first) {
        this._first = false;
        return { value: "bye", done: false };
      } else {
        return { done: true };
      }
    },
    _first: true
  };
};

[...str] // ["bye"]
str // "hi"
// 由于扩展运算符(...)使用时会默认调用 iterator 接口,而 iterator 部署
// 在 Symbol.iterator 上,所以此时我们会执行 Symbol.iterator 内的方法
// 在手动更改了他的属性之后,可以看到有明显不同的效果

Iterator 接口与 Generator 函数
let myIterable = {
  [Symbol.iterator]: function* () {
    yield 1;
    yield 2;
    yield 3;
  }
}
[...myIterable] // [1, 2, 3]
// Generator函数会在下一篇博客详细的介绍

for…of 循环
  • 一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法。

  • for…of循环可以使用的范围包括数组Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、Generator 对象,以及字符串

  • 数组

let newerarrray = [1, 2, 3, 4]
for(let value of newerarrray){
    console.log(value)
    // 1   2   3    4
}
--------------------------------------------------------------------
//for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性
let newerarrray = [1, 2, 3, 4]
newerarrray.foo = 'hello'

for(let value of newerarrray){
    console.log(value)
    // [1, 2, 3, 4]
}
// 此时的数组结构 [1, 2, 3, 4, foo:'hello'],由于 foo不是数字索引,所以不返回'hello'
--------------------------------------------------------------------
//for...of循环读取键值。如果要通过for...of循环,获取数组的索引,可以借助
//数组实例的entries方法和keys方法
newerarrray = ['a', 'b', 'c']
for(let value of newerarrray.entries()){
    console.log(value)
    // [0,'a'], [1, 'b'], [2, 'c']
    // 输出同时具有索引和值得数组
}

for(let value of newerarrray.keys()){
    console.log(value)
    // 0 1 2
    // 输出每个值得索引
}
  • Set 和 Map 结构
// Set 结构遍历时,返回的是一个值
let newset = new Set([1, 2, 2, 3])
for (let value of newset){
	console.log(value)
	// 1, 2, 3
}

// Map 结构遍历时,返回的是一个数组,该数组的两个成员分别为当前 Map 成员
// 的键名和键值。
let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
  console.log(pair);
}
// ['a', 1]
// ['b', 2]
  • 类似数组的对象
// 字符串
let str = "hello";

for (let s of str) {
  console.log(s); // h e l l o
}

// DOM NodeList对象
let paras = document.querySelectorAll("p");

for (let p of paras) {
  p.classList.add("test");
  // 为不同的 p标签 添加新的类名,如已经存在,取消添加
}

// arguments对象
function printArgs() {
  for (let x of arguments) {
    console.log(x);
  }
}
printArgs('a', 'b');
// 'a'
// 'b'
-------------------------------------------------------------------
//当遇到类数组对象不能够用 for...of遍历时,我们可以将其转化为数组
```javascript
let arrayLike = { length: 2, 0: 'a', 1: 'b' };

for (let x of Array.from(arrayLike)) {
	......
}
  • 对象
//对于普通的对象,for...of结构不能直接使用,会报错,必须部署了 Iterator 接
//口后才能使用
let stu = {
	name : 'Jack',
	age : 18
}
for (let value of stu){}  // Error
// 部署Iterator的方法 => 添加 Symbol.iterator属性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值