Iterator(迭代器)

Iterator是一种接口,为各种不同的数据结构提供统一的访问机制。
任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
一个数据结构只要具有 iterator 接口,就可以用for...of循环遍历它的成员。

并不是所有的对象都能使用for...of循环,只有实现了Iterator接口的对象,才能够for...of来进行遍历取值。
作用
    1. 为各种数据结构,提供一个统一的、简便的访问接口;
    2. 使得数据结构的成员能够按某种次序排列;
    3. 创造一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。
Iterator的产生主要是为了使用for...of方法
Iterator其实就是一个具有 next()方法的对象,并且每次调用next()方法都会返回一个结果对象,这个结果对象有两个属性

{ 
    value: 表示当前的值,  
    done: 表示遍历是否结束
}
Iterator是一个特殊的对象:
    1. 它具有next()方法,调用该方法就会返回一个结果对象
    2. 结果对象有两个属性值:value和done。    
    3. value表示具体的返回值;done是布尔类型,表示集合是否完成遍历,没有则返回true,否则返回false
    4. 内部有一个指针,指向数据结构的起始位置。每调用一次next()方法,指针都会向后移动一个位置,直到指向最后一个位置

模拟Iterator

 function createIterator(items) {
    var i = 0;
    return {
      next: function () {
        var done = (i >= item.length);
        var value = !done ? items[i++] : undefined;


        return {
          done: done,
          value: value
        };
      },
      return(){ // 可选return方法
        console.log('提前退出');
        return {// 必须返回一个对象
            done:true
        }
      },
      // return: function () {
      //   return {
      //      done:true
      //   };
      // },

    };
  }

  // iterator 就是一个迭代器对象
  var iterator = createIterator([1, 2, 3]);

  console.log(iterator.next()); // { done: false, value: 1 }
  console.log(iterator.next()); // { done: false, value: 2 }
  console.log(iterator.next()); // { done: false, value: 3 }
  console.log(iterator.next()); // { done: true, value: undefined }
  console.log(iterator.next()); // { done: true, value: undefined }
  console.log(iterator.next()); // { done: true, value: undefined }


  var iterator = makeIterator([1, 2, 3]);

  // 不可以被for of 循环
  for (let value of iterator) {
    console.log(value); // Uncaught TypeError: iterator is not iterable
  }

可迭代对象Iterable

ES6还引入了一个新的Symbol对象,symbol值是唯一的。
一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口;
调用这个接口,就会返回一个遍历器对象。这样的数据结构才能被称为可迭代对象(Iterator),该对象可被for...of遍历

  var arr = [1, 2, 3];

  arr[Symbol.iterator] = function () {
    var _this = this;
    var i = 0;
    return {
      next: function () {
        var done = (i >= _this.length);
        var value = !done ? _this[i++] : undefined;
        return {
          done: done,
          value: value
        };
      }
    };
  }


  // 此时可以for...of遍历
  for (var item of arr) {
    console.log(item); // 1 2 3 
  }

  // for...of 遍历的其实是对象的 Symbol.iterator 属性
原生具备Iterator接口的数据结构

在ES6中,所有的集合对象,包括
    数组,
    类数组对象(arguments对象、DOM NodeList 对象),
    Map
    Set
    字符串
都是可迭代的,都可以被for...of遍历,因为他们都有默认的迭代器。
数组

  var arr = [1, 2, 3];
  var iteratorObj = arr[Symbol.iterator]();
  console.log(iteratorObj.next());
  console.log(iteratorObj.next());
  console.log(iteratorObj.next());
  console.log(iteratorObj.next());
arguments

  function test() {
    var obj = arguments[Symbol.iterator]();
    console.log(arguments);
    console.log(obj.next());
    console.log(obj.next());
    console.log(obj.next());
  }

  test(1, 2, 3);
NodeList

  <div class="test">1</div>
  <div class="test">2</div>
  <div class="test">3</div>

  const nodeList = document.getElementsByClassName('test')
  for (const node of nodeList) {
    console.log(node);
  }
map
  console.log(Map.prototype.hasOwnProperty(Symbol.iterator)); // true

调用 Iterator 接口的场合

有一些场合会默认调用 Iterator 接口(即Symbol.iterator方法)
解构赋值

  let set = new Set().add('a').add('b').add('c’);

  let [x, y] = set;
  console.log(x, y); // a b
扩展运算符

  var str = 'hello';
  console.log([...str]); // ["h", "e", "l", "l", "o"]

  let arr = ['b', 'c'];
  console.log(['a', ...arr, 'd']); // ["a", "b", "c", "d"]
yield*

  // yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。

  let generator = function* () {
    yield 1;
    yield*[2, 3, 4];
    yield 5;
  };


  var iterator = generator();


  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: 4, done: false }
  console.log(iterator.next()); // { value: 5, done: false }
  console.log(iterator.next()); // { value: undefined, done: true }


  function* entries(obj) {
    for (let key of Object.keys(obj)) {
      yield [key, obj[key]];
    }
  }

  for (let [key, value] of entries(obj)) {
    console.log(key, '->', value);
  }
  // a -> 1
  // b -> 2
  // c -> 3
其他

    // for...of、Set()、Map()、Array.from()等。我们主要是为了证明调用上面这些函数时着实是用到了Iterator接口。

  var arr = [1, 2, 3];

  arr[Symbol.iterator] = function () {
    var _this = this;
    var i = 0;
    return {
      next: function () {
        var done = (i >= _this.length);
        var value = !done ? _this[i++] : undefined;
        return {
          done: done,
          value: value + ' 手动添加的属性' // 添加自定义值
        };
      }
    };
  }


  // 输出结果显示手动修改生效,证明for...of调用了Iterator接口
  for (var item of arr) {
    console.log(item);
    // 1 手动添加的属性
    // 2 手动添加的属性
    // 3 手动添加的属性
  }


  var set = new Set(arr);
  console.log(set); // Set(3) {"1 手动添加的属性", "2 手动添加的属性", "3 手动添加的属性"}

计算生成的数据结构

有些数据结构是在现有数据结构的基础上,计算生成的。比如,ES6 的数组、Set、Map 都部署了以下三个方法,调用后都返回遍历器对象。
    1. entries() 返回一个遍历器对象,用来遍历[键名,键值]组成的数组。对于数组,键名就是索引值。
    2. keys() 返回一个遍历器对象,用来遍历所有的键名。
    3. values() 返回一个遍历器对象,用来遍历所有的键值。
 var arr = ["first", "second", "third"];


  for (let index of arr.keys()) {
    console.log(index);
  }

  // 0
  // 1
  // 2


  for (let color of arr.values()) {
    console.log(color);
  }

  // first
  // second
  // third


  for (let item of arr.entries()) {
    console.log(item);
  }

  // [ 0, "first" ]
  // [ 1, "second" ]
  // [ 2, "third" ]

判断对象是否可迭代

  const isIterable = obj => obj != null && typeof obj[Symbol.iterator] === 'function';
  console.log(isIterable(new Set())); // true 
  console.log(isIterable(new Map())); // true  
  console.log(isIterable([1, 2, 3])); // true 
  console.log(isIterable("hello world")); // true

for…of循环不支持遍历普通对象

Iterator的产生主要是为了使用for...of方法。
而对象不像数组的值是有序的,遍历的时候根本不知道如何确定他们的先后顺序,所以要注意的是for...of循环不支持遍历对象。
如果非要遍历对象,同理对象也必须包含[Symbol.iterator]的属性并实现迭代器方法,可以通过手动利用Object.defineProperty方法添加该属性。
  var obj = {
    a: 2,
    b: 3
  }
  for (let i of obj) {
    console.log(i) // Uncaught TypeError: obj is not iterable
  }
类数组对象
  var obj = {
    0: 'one',
    1: 'two',
    length: 2
  };
  obj = Array.from(obj);
  for (var k of obj) {
    console.log(k)
  }
非类数组对象

// 方法一
  var obj = {
    a: 1,
    b: 2,
    c: 3
  };


  obj[Symbol.iterator] = function () {
    var keys = Object.keys(this);
    var count = 0;
    return {
      next() {
        if (count < keys.length) {
          return {
            value: obj[keys[count++]],
            done: false
          };
        } else {
          return {
            value: undefined,
            done: true
          };
        }
      }
    }
  };


  for (var k of obj) {
    console.log(k);
  }


// 方法二
  var obj = {
    a: 1,
    b: 2,
    c: 3
  };
  obj[Symbol.iterator] = function* () {
    var keys = Object.keys(obj);
    for (var k of keys) {
      yield [k, obj[k]]
    }
  };


  for (var [k, v] of obj) {
    console.log(k, v);
  }
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值