【澳门葡京】es六 语法 (iterator和for…of循环)

Iterator遍历器

遍历器(Iterator)就是这般壹种机制。它是一种接口,为各个分裂的数据结构提供联合的访问机制。任何数据结构只要安顿Iterator接口,就能够形成遍历操作(即依次拍卖该数据结构的持有成员)。

作用:

  • 为各样数据结构,提供一个统1的、简便的拜会接口
  • 使得数据结构的成员能够按某种次序排列
  • ES6开立了一种新的遍历命令for...of巡回,Iterator接口主要供for...of消费

Iterator的遍历过程:

(1)创制贰个指针对象,指向当前数据结构的胚胎地方。也正是说,遍历器对象本质上,就是3个指针对象。

(二)第③回调用指针对象的next措施,能够将指针指向数据结构的率先个成员。

(叁)第贰回调用指针对象的next艺术,指针就对准数据结构的第三个分子。

(4)不断调用指针对象的next措施,直到它指向数据结构的利落地点。

在ES陆中,有三类数据结构原生拥有Iterator接口:数组、有个别类似数组的对象、Set和Map结构。

能够覆盖原生的Symbol.iterator艺术,到达修改遍历器行为的目标。

Iterator遍历器

遍历器(Iterator)就是这么壹种机制。它是1种接口,为各个分化的数据结构提供联合的访问机制。任何数据结构只要陈设Iterator接口,就足以实现遍历操作(即依次拍卖该数据结构的保有成员)。

作用:

  • 为各样数据结构,提供2个联结的、简便的走访接口
  • 使得数据结构的分子能够按某种次序排列
  • ES6成立了1种新的遍历命令for...of循环,Iterator接口主要供for...of消费

Iterator的遍历进度:

(1)创建一个指针对象,指向当前数据结构的开局地方。约等于说,遍历器对象本质上,就是3个指针对象。

(二)第贰次调用指针对象的next方式,能够将指针指向数据结构的首先个成员。

(3)首次调用指针对象的next格局,指针就本着数据结构的第三个成员。

(四)不断调用指针对象的next艺术,直到它指向数据结构的终结地点。

在ES陆中,有3类数据结构原生具备Iterator接口:数组、有些类似数组的目的、Set和Map结构。

能够覆盖原生的Symbol.iterator主意,到达修改遍历器行为的目的。

Iterator和for…of循环

  1. Iterator(遍历器)的概念
  2. 数据结构的暗中认可Iterator接口
  3. 调用Iterator接口的场所
  4. 字符串的Iterator接口
  5. Iterator接口与Generator函数
  6. 遍历器对象的return(),throw()%EF%BC%8Cthrow())
  7. for…of循环

Iterator(遍历器)的概念

JavaScript原有的象征“群集”的数据结构,首若是数组(Array)和目的(Object),ES六又增加了Map和Set。那样就有了多种多少集结,用户还是能整合使用它们,定义自身的数据结构,比方数组的分子是Map,Map的分子是目的。那样就须求壹种统一的接口机制,来拍卖全数区别的数据结构。

遍历器(Iterator)正是这么1种体制。它是1种接口,为各类分裂的数据结构提供联合的拜会机制。任何数据结构只要安排Iterator接口,就足以成功遍历操作(即依次拍卖该数据结构的具有成员)。

Iterator的功效有四个:壹是为各类数据结构,提供多个集合的、简便的拜会接口;二是驱动数据结构的积极分子能够按某种次序排列;三是ES六开立了壹种新的遍历命令for...of循环,Iterator接口首要供for...of消费。

Iterator的遍历进度是如此的。

(1)创造3个指南针对象,指向当前数据结构的初步地点。相当于说,遍历器对象本质上,便是2个指南针对象。

(二)第贰回调用指针对象的next措施,能够将指针指向数据结构的率先个成员。

(三)第三回调用指针对象的next措施,指针就针对数据结构的第三个成员。

(四)不断调用指针对象的next主意,直到它指向数据结构的甘休地方。

每次调用next主意,都会回到数据结构的当下成员的新闻。具体来讲,正是重返一个带有valuedone两个天性的目的。当中,value脾性是当下成员的值,done质量是二个布尔值,表示遍历是或不是结束。

上面是三个模拟next艺术重临值的例子。

var it = makeIterator(['a', 'b']);

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }

function makeIterator(array) {
  var nextIndex = 0;
  return {
    next: function() {
      return nextIndex < array.length ?
        {value: array[nextIndex++], done: false} :
        {value: undefined, done: true};
    }
  };
}

上面代码定义了三个makeIterator函数,它是一个遍历器生成函数,成效正是回去1个遍历器对象。对数组['a', 'b']推行这些函数,就会回去该数组的遍历器对象(即指针对象)it

指南针对象的next办法,用来移动指针。发轫时,指针指向数组的发轫地方。然后,每一次调用next方法,指针就会指向数组的下一个分子。第壹回调用,指向a;第1回调用,指向b

next格局重回2个目标,表示近期多少成员的消息。那个目标具备valuedone五个天性,value天性重回当前地方的成员,done性格是一个布尔值,表示遍历是还是不是终止,便是或不是还有供给再一回调用next方法。

简单的说,调用指针对象的next艺术,就足以遍历事先给定的数据结构。

对此遍历器对象的话,done: falsevalue: undefined本性都以足以总结的,因而地点的makeIterator函数能够简写成上面包车型大巴款型。

function makeIterator(array) {
  var nextIndex = 0;
  return {
    next: function() {
      return nextIndex < array.length ?
        {value: array[nextIndex++]} :
        {done: true};
    }
  };
}

出于Iterator只是把接口标准加到数据结构之上,所以,遍历器与它所遍历的老大数据结构,实际上是分其余,完全能够写出从未对号入座数据结构的遍历器对象,大概说用遍历器对象模拟出数据结构。上边是一个最棒运转的遍历器对象的例子。

var it = idMaker();

it.next().value // '0'
it.next().value // '1'
it.next().value // '2'
// ...

function idMaker() {
  var index = 0;

  return {
    next: function() {
      return {value: index++, done: false};
    }
  };
}

地点的例子中,遍历器生成函数idMaker,重回一个遍历器对象(即指针对象)。然则并未相应的数据结构,或许说,遍历器对象自身描述了2个数据结构出来。

在ES陆中,有个别数据结构原生具有Iterator接口(比方数组),即不用此外管理,就可以被for...of巡回遍历,有个别就特别(例如对象)。原因在于,那一个数据结构原生安排了Symbol.iterator属性(详见下文),其它一些数据结构未有。凡是陈设了Symbol.iterator品质的数据结构,就叫做安排了遍历器接口。调用那几个接口,就会回到三个遍历器对象。

假设运用TypeScript的写法,遍历器接口(Iterable)、指针对象(Iterator)和next方法重返值的条件能够描述如下。

interface Iterable {
  [Symbol.iterator]() : Iterator,
}

interface Iterator {
  next(value?: any) : IterationResult,
}

interface IterationResult {
  value: any,
  done: boolean,
}

澳门葡京 ,for…of

for…of循环能够应用的限量包涵数组、Set和Map结构、有个别类似数组的对象(比如arguments对象、DOM
NodeList对象)、后文的Generator对象,以及字符串。

 

{
  let arr=['hello','world'];
  let map=arr[Symbol.iterator]();
  //done表示是否还有下一步了,false有 true 没有
  console.log(map.next()); //{value: "hello", done: false}
  console.log(map.next()); //{value: "world", done: false}
  console.log(map.next()); //{value: undefined, done: true}
}

{
  let obj={
    start:[1,3,2],
    end:[7,9,8],
    //声明
    [Symbol.iterator](){
      //函数体
      let self=this;
      let index=0; //当前遍历索引
      let arr=self.start.concat(self.end); //合并数组
      let len=arr.length;//记住数组长度
      return {
        //iterator部署的时候一定要有next这个方法
        next(){
          //遍历过程
          if(index<len){
            return {
              value:arr[index++],
              done:false
            }
          }else{
            return {
              value:arr[index++],
              done:true //遍历结束
            }
          }
        }
      }
    }
  }
  //验证接口是否部署成功
  for(let key of obj){
    console.log('key1',key); //1 3 2 7 9 8
  }
}

{
  let arr=['hello','world'];
  for(let value of arr){
    console.log('value',value); //hello ,world
  }
}

 

 

for…of

for…of循环能够使用的范围包含数组、Set和Map结构、某个类似数组的靶子(比如arguments对象、DOM
NodeList对象)、后文的Generator对象,以及字符串。

 

{
  let arr=['hello','world'];
  let map=arr[Symbol.iterator]();
  //done表示是否还有下一步了,false有 true 没有
  console.log(map.next()); //{value: "hello", done: false}
  console.log(map.next()); //{value: "world", done: false}
  console.log(map.next()); //{value: undefined, done: true}
}

{
  let obj={
    start:[1,3,2],
    end:[7,9,8],
    //声明
    [Symbol.iterator](){
      //函数体
      let self=this;
      let index=0; //当前遍历索引
      let arr=self.start.concat(self.end); //合并数组
      let len=arr.length;//记住数组长度
      return {
        //iterator部署的时候一定要有next这个方法
        next(){
          //遍历过程
          if(index<len){
            return {
              value:arr[index++],
              done:false
            }
          }else{
            return {
              value:arr[index++],
              done:true //遍历结束
            }
          }
        }
      }
    }
  }
  //验证接口是否部署成功
  for(let key of obj){
    console.log('key1',key); //1 3 2 7 9 8
  }
}

{
  let arr=['hello','world'];
  for(let value of arr){
    console.log('value',value); //hello ,world
  }
}

 

 

Iterator(遍历器)的概念

JavaScript原有的表示“集结”的数据结构,首假设数组(Array)和目的(Object),ES陆又加多了Map和Set。那样就有了多种多少群集,用户还足以组合使用它们,定义自个儿的数据结构,比方数组的成员是Map,Map的成员是目的。那样就须要一种统一的接口机制,来管理全数分裂的数据结构。

遍历器(Iterator)正是那样1种体制。它是1种接口,为各样分化的数据结构提供联合的走访机制。任何数据结构只要布置Iterator接口,就足以做到遍历操作(即依次拍卖该数据结构的享有成员)。

Iterator的作用有八个:1是为各类数据结构,提供1个集结的、简便的造访接口;二是驱动数据结构的积极分子能够按某种次序排列;三是ES陆开立了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。

Iterator的遍历进程是如此的。

(1)创设一个指针对象,指向当前数据结构的序曲地方。也等于说,遍历器对象本质上,正是二个指针对象。

(二)第2遍调用指针对象的next艺术,能够将指针指向数据结构的第一个分子。

(三)第二次调用指针对象的next艺术,指针就本着数据结构的第二个分子。

(四)不断调用指针对象的next艺术,直到它指向数据结构的了断地点。

每回调用next措施,都会回到数据结构的当下成员的新闻。具体来讲,便是重回2个含有valuedone八个属性的目的。在那之中,value个性是时下成员的值,done性子是二个布尔值,表示遍历是还是不是终止。

下边是叁个仿照next艺术重返值的例证。

var it = makeIterator(['a', 'b']);

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }

function makeIterator(array) {
  var nextIndex = 0;
  return {
    next: function() {
      return nextIndex < array.length ?
        {value: array[nextIndex++], done: false} :
        {value: undefined, done: true};
    }
  };
}

上面代码定义了叁个makeIterator函数,它是多个遍历器生成函数,成效正是回来一个遍历器对象。对数组['a', 'b']实践那个函数,就会回来该数组的遍历器对象(即指针对象)it

指南针对象的next方法,用来运动指针。伊始时,指针指向数组的发端地方。然后,每一回调用next情势,指针就会指向数组的下3个成员。第二回调用,指向a;第一遍调用,指向b

next格局再次回到1个对象,表示近日多少成员的新闻。那么些目的具备valuedone七个属性,value品质再次回到当前职分的成员,done属性是一个布尔值,表示遍历是不是得了,就是不是还有供给再二次调用next方法。

一言以蔽之,调用指针对象的next艺术,就可以遍历事先给定的数据结构。

对此遍历器对象的话,done: falsevalue: undefined个性都是足以省略的,因而地点的makeIterator函数能够简写成上边的款型。

function makeIterator(array) {
  var nextIndex = 0;
  return {
    next: function() {
      return nextIndex < array.length ?
        {value: array[nextIndex++]} :
        {done: true};
    }
  };
}

是因为Iterator只是把接口标准加到数据结构之上,所以,遍历器与它所遍历的分外数据结构,实际上是分开的,完全能够写出从未相应数据结构的遍历器对象,只怕说用遍历器对象模拟出数据结构。下边是一个十分运维的遍历器对象的事例。

var it = idMaker();

it.next().value // '0'
it.next().value // '1'
it.next().value // '2'
// ...

function idMaker() {
  var index = 0;

  return {
    next: function() {
      return {value: index++, done: false};
    }
  };
}

地方的事例中,遍历器生成函数idMaker,再次回到一个遍历器对象(即指针对象)。然则并从未对号入座的数据结构,或然说,遍历器对象自身描述了多个数据结构出来。

在ES陆中,有个别数据结构原生具有Iterator接口(比方数组),即不用别样管理,就足以被for...of巡回遍历,有个别就可怜(比方对象)。原因在于,那些数据结构原生安顿了Symbol.iterator性格(详见下文),其余一些数据结构没有。凡是计划了Symbol.iterator属性的数据结构,就称为铺排了遍历器接口。调用这些接口,就会重临三个遍历器对象。

假诺运用TypeScript的写法,遍历器接口(Iterable)、指针对象(Iterator)和next方法重返值的尺度能够描述如下。

interface Iterable {
  [Symbol.iterator]() : Iterator,
}

interface Iterator {
  next(value?: any) : IterationResult,
}

interface IterationResult {
  value: any,
  done: boolean,
}

数据结构的默许Iterator接口

Iterator接口的目标,便是为全数数据结构,提供了壹种统壹的造访机制,即for...of循环(详见下文)。当使用for...of巡回遍历某种数据结构时,该循环会自动去追寻Iterator接口。

1种数据结构只要安顿了Iterator接口,大家就称那种数据结构是”可遍历的“(iterable)。

ES六分明,暗许的Iterator接口计划在数据结构的Symbol.iterator本性,可能说,贰个数据结构只要持有Symbol.iterator性情,就足以以为是“可遍历的”(iterable)。Symbol.iterator属性本人是二个函数,就是当前数据结构暗许的遍历器生成函数。施行那么些函数,就会回到一个遍历器。至于属性名Symbol.iterator,它是3个表达式,重返Symbol对象的iterator属性,那是一个预订义好的、类型为Symbol的万分值,所以要放在方括号内。(参见Symbol一章)。

const obj = {
  [Symbol.iterator] : function () {
    return {
      next: function () {
        return {
          value: 1,
          done: true
        };
      }
    };
  }
};

上面代码中,对象obj是可遍历的(iterable),因为具备Symbol.iterator品质。施行那个特性,会回到一个遍历器对象。该对象的向来特征就是怀有next主意。每趟调用next情势,都会回来贰个意味当前成员的音讯目的,具备valuedone两本性子。

在ES六中,有三类数据结构原生具有Iterator接口:数组、有些类似数组的对象、Set和Map结构。

let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

地方代码中,变量arr是二个数组,原生就具备遍历器接口,计划在arrSymbol.iterator质量下边。所以,调用那个天性,就赢得遍历器对象。

下面提到,原生就布局Iterator接口的数据结构有三类,对于那3类数据结构,不用自身写遍历器生成函数,for...of循环会自动遍历它们。除外,其余数据结构(首要是目的)的Iterator接口,都急需和谐在Symbol.iterator质量上面安顿,那样才会被for...of循环遍历。

对象(Object)之所以未有默许陈设Iterator接口,是因为对象的哪些属性先遍历,哪个属性后遍历是不明确的,要求开荒者手动钦定。本质上,遍历器是一种线性管理,对于任何非线性的数据结构,安顿遍历器接口,就分外安插一种线性转变。可是,严酷地说,对象铺排遍历器接口并不是很供给,因为此时对象实际被视作Map结构选拔,ES5并未有Map结构,而ES陆原生提供了。

三个目的假使要有可被for...of循环调用的Iterator接口,就必须在Symbol.iterator【澳门葡京】es六 语法 (iterator和for…of循环)。的属性上配备遍历器生成方法(原型链上的目的具备该办法也可)。

class RangeIterator {
  constructor(start, stop) {
    this.value = start;
    this.stop = stop;
  }

  [Symbol.iterator]() { return this; }

  next() {
    var value = this.value;
    if (value < this.stop) {
      this.value++;
      return {done: false, value: value};
    }
    return {done: true, value: undefined};
  }
}

function range(start, stop) {
  return new RangeIterator(start, stop);
}

for (var value of range(0, 3)) {
  console.log(value);
}

上边代码是多少个类安插Iterator接口的写法。Symbol.iterator属性对应多少个函数,执行后归来当前线指挥部标的遍历器对象。

上面是透过遍历器完毕指针结构的例证。

function Obj(value) {
  this.value = value;
  this.next = null;
}

Obj.prototype[Symbol.iterator] = function() {
  var iterator = {
    next: next
  };

  var current = this;

  function next() {
    if (current) {
      var value = current.value;
      current = current.next;
      return {
        done: false,
        value: value
      };
    } else {
      return {
        done: true
      };
    }
  }
  return iterator;
}

var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);

one.next = two;
two.next = three;

for (var i of one){
  console.log(i);
}
// 1
// 2
// 3

地点代码首先在构造函数的原型链上安排Symbol.iterator措施,调用该方法会再次来到遍历器对象iterator,调用该目标的next主意,在重返四个值的同时,自动将中间指针移到下1个实例。

上面是另三个为对象增多Iterator接口的事例。

let obj = {
  data: [ 'hello', 'world' ],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return {
            value: self.data[index++],
            done: false
          };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};

对此类似数组的对象(存在数值键名和length属性),计划Iterator接口,有二个轻巧方法,就是Symbol.iterator办法间接引用数组的Iterator接口。

NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];

[...document.querySelectorAll('div')] // 可以执行了

上面是周边数组的目的调用数组的Symbol.iterator方法的例子。

let iterable = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // 'a', 'b', 'c'
}

专注,普通对象安排数组的Symbol.iterator艺术,并无效果。

let iterable = {
  a: 'a',
  b: 'b',
  c: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // undefined, undefined, undefined
}

如果Symbol.iterator办法对应的不是遍历器生成函数(即会重回三个遍历器对象),解释引擎将会报错。

var obj = {};

obj[Symbol.iterator] = () => 1;

[...obj] // TypeError: [] is not a function

地点代码中,变量obj的Symbol.iterator方法对应的不是遍历器生成函数,由此报错。

有了遍历器接口,数据结构就足以用for...of巡回遍历(详见下文),也能够动用while巡回遍历。

var $iterator = ITERABLE[Symbol.iterator]();
var $result = $iterator.next();
while (!$result.done) {
  var x = $result.value;
  // ...
  $result = $iterator.next();
}

地点代码中,ITERABLE意味着某种可遍历的数据结构,$iterator是它的遍历器对象。遍历器对象每一回运动指针(next措施),都检查一下重临值的done性格,假使遍历还没甘休,就活动遍历器对象的指针到下一步(next措施),不断循环。

数据结构的默许Iterator接口

Iterator接口的目标,便是为全部数据结构,提供了一种统一的造访机制,即for...of巡回(详见下文)。当使用for...of循环遍历某种数据结构时,该循环会自动去研究Iterator接口。

一种数据结构只要安插了Iterator接口,大家就称那种数据结构是”可遍历的“(iterable)。

ES陆鲜明,暗中认可的Iterator接口计划在数据结构的Symbol.iterator品质,可能说,二个数据结构只要持有Symbol.iterator质量,就能够以为是“可遍历的”(iterable)。Symbol.iterator性能本身是一个函数,正是近日数据结构默许的遍历器生成函数。执行这些函数,就会回来一个遍历器。至于属性名Symbol.iterator,它是1个说明式,再次来到Symbol对象的iterator属性,这是四个预订义好的、类型为Symbol的分裂平时值,所以要放在方括号内。(参见Symbol一章)。

const obj = {
  [Symbol.iterator] : function () {
    return {
      next: function () {
        return {
          value: 1,
          done: true
        };
      }
    };
  }
};

上边代码中,对象obj是可遍历的(iterable),因为具有Symbol.iterator品质。施行这性情子,会回去叁个遍历器对象。该对象的常有特征正是享有next措施。每一回调用next方法,都会再次回到三个代表当前成员的音信目的,具备valuedone五个属性。

在ES陆中,有三类数据结构原生具备Iterator接口:数组、有个别类似数组的目的、Set和Map结构。

let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

下面代码中,变量arr是三个数组,原生就有所遍历器接口,铺排在arrSymbol.iterator天性下面。所以,调用那一个天性,就收获遍历器对象。

地方提到,原生就布局Iterator接口的数据结构有三类,对于那3类数据结构,不用本身写遍历器生成函数,for...of循环会自动遍历它们。除了那个之外,其余数据结构(首借使目的)的Iterator接口,都亟需团结在Symbol.iterator本性上边陈设,这样才会被for...of循环遍历。

目的(Object)之所以未有默许布署Iterator接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不分明的,须求开荒者手动钦赐。本质上,遍历器是1种线性管理,对于别的非线性的数据结构,安插遍历器接口,就等于陈设1种线性转变。但是,严谨地说,对象安插遍历器接口并不是很供给,因为那时对象实际被看成Map结构采用,ES伍不曾Map结构,而ES陆原生提供了。

3个目的要是要有可被for...of巡回调用的Iterator接口,就亟须在Symbol.iterator的本性上配置遍历器生成方法(原型链上的目的具备该格局也可)。

class RangeIterator {
  constructor(start, stop) {
    this.value = start;
    this.stop = stop;
  }

  [Symbol.iterator]() { return this; }

  next() {
    var value = this.value;
    if (value < this.stop) {
      this.value++;
      return {done: false, value: value};
    } else {
      return {done: true, value: undefined};
    }
  }
}

function range(start, stop) {
  return new RangeIterator(start, stop);
}

for (var value of range(0, 3)) {
  console.log(value);
}

地方代码是1个类布署Iterator接口的写法。Symbol.iterator性格对应二个函数,推行后归来当前线指挥部标的遍历器对象。

下边是通过遍历器完成指针结构的例证。

function Obj(value) {
  this.value = value;
  this.next = null;
}

Obj.prototype[Symbol.iterator] = function() {
  var iterator = {
    next: next
  };

  var current = this;

  function next() {
    if (current) {
      var value = current.value;
      current = current.next;
      return {
        done: false,
        value: value
      };
    } else {
      return {
        done: true
      };
    }
  }
  return iterator;
}

var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);

one.next = two;
two.next = three;

for (var i of one){
  console.log(i);
}
// 1
// 2
// 3

上边代码首先在构造函数的原型链上安插Symbol.iterator措施,调用该方法会再次来到遍历器对象iterator,调用该目的的next办法,在回去三个值的同时,自动将里面指针移到下二个实例。

下边是另3个为目的加多Iterator接口的事例。

let obj = {
  data: [ 'hello', 'world' ],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return {
            value: self.data[index++],
            done: false
          };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};

对于类似数组的对象(存在数值键名和length属性),布置Iterator接口,有二个便当方法,便是Symbol.iterator办法直接引用数组的Iterator接口。

NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];

[...document.querySelectorAll('div')] // 可以执行了

上边是看似数组的目的调用数组的Symbol.iterator格局的事例。

let iterable = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // 'a', 'b', 'c'
}

小心,普通对象计划数组的Symbol.iterator措施,并无效益。

let iterable = {
  a: 'a',
  b: 'b',
  c: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // undefined, undefined, undefined
}

如果Symbol.iterator方法对应的不是遍历器生成函数(即会回来贰个遍历器对象),解释引擎将会报错。

var obj = {};

obj[Symbol.iterator] = () => 1;

[...obj] // TypeError: [] is not a function

上面代码中,变量obj的Symbol.iterator方法对应的不是遍历器生成函数,由此报错。

有了遍历器接口,数据结构就能够用for...of循环遍历(详见下文),也得以利用while循环遍历。

var $iterator = ITERABLE[Symbol.iterator]();
var $result = $iterator.next();
while (!$result.done) {
  var x = $result.value;
  // ...
  $result = $iterator.next();
}

地方代码中,ITERABLE表示某种可遍历的数据结构,$iterator是它的遍历器对象。遍历器对象每趟运动指针(next主意),都检查一下再次来到值的done属性,假若遍历还没得了,就活动遍历器对象的指针到下一步(next主意),不断循环。

调用Iterator接口的地方

有一些场子会私下认可调用Iterator接口(即Symbol.iterator主意),除了下文种介绍的for...of循环,还有多少个别的地方。

(一)解构赋值

对数组和Set结构进行解构赋值时,会暗中同意调用Symbol.iterator方法。

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

let [x,y] = set;
// x='a'; y='b'

let [first, ...rest] = set;
// first='a'; rest=['b','c'];

(2)扩张运算符

扩大运算符(…)也会调用私下认可的iterator接口。

// 例一
var str = 'hello';
[...str] //  ['h','e','l','l','o']

// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']

地点代码的恢弘运算符内部就调用Iterator接口。

实则,那提供了一种便利机制,能够将其他铺排了Iterator接口的数据结构,转为数组。也正是说,只要有些数据结构铺排了Iterator接口,就足以对它使用扩充运算符,将其转为数组。

let arr = [...iterable];

(3)yield*

yield*前边跟的是1个可遍历的结构,它会调用该组织的遍历器接口。

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

var iterator = generator();

iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }

(四)别的场面

出于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。上边是某个例子。

  • for…of
  • Array.from()
  • Map(), Set(), WeakMap(),
    WeakSet()(比如new Map([['a',1],['b',2]])
  • Promise.all()
  • Promise.race()

调用Iterator接口的地方

有局地场子会私下认可调用Iterator接口(即Symbol.iterator主意),除了下文子禽介绍的for...of循环,还有多少个其他场馆。

(1)解构赋值

对数组和Set结构进行解构赋值时,会默许调用Symbol.iterator方法。

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

let [x,y] = set;
// x='a'; y='b'

let [first, ...rest] = set;
// first='a'; rest=['b','c'];

(二)扩大运算符

推而广之运算符(…)也会调用暗中同意的iterator接口。

// 例一
var str = 'hello';
[...str] //  ['h','e','l','l','o']

// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']

地点代码的扩展运算符内部就调用Iterator接口。

实则,那提供了1种方便人民群众机制,能够将别的布署了Iterator接口的数据结构,转为数组。也正是说,只要有个别数据结构计划了Iterator接口,就足以对它采纳扩展运算符,将其转为数组。

let arr = [...iterable];

(3)yield*

yield*末尾跟的是多个可遍历的结构,它会调用该组织的遍历器接口。

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

var iterator = generator();

iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }

(四)别的地方

鉴于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场馆,其实都调用了遍历器接口。上边是1对例子。

  • for…of
  • Array.from()
  • Map(), Set(), WeakMap(),
    WeakSet()(比如new Map([['a',1],['b',2]])
  • Promise.all()
  • Promise.race()

字符串的Iterator接口

字符串是3个看似数组的对象,也原生具备Iterator接口。

var someString = "hi";
typeof someString[Symbol.iterator]
// "function"

var iterator = someString[Symbol.iterator]();

iterator.next()  // { value: "h", done: false }
iterator.next()  // { value: "i", done: false }
iterator.next()  // { value: undefined, done: true }

地点代码中,调用Symbol.iterator主意再次来到三个遍历器对象,在这一个遍历器上得以调用next方法,达成对于字符串的遍历。

能够覆盖原生的Symbol.iterator主意,达到修改遍历器行为的目标。

var str = new String("hi");

[...str] // ["h", "i"]

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"

上边代码中,字符串str的Symbol.iterator办法被改造了,所以增加运算符(...)再次回到的值形成了bye,而字符串本人依然hi

字符串的Iterator接口

字符串是三个近乎数组的对象,也原生具备Iterator接口。

var someString = "hi";
typeof someString[Symbol.iterator]
// "function"

var iterator = someString[Symbol.iterator]();

iterator.next()  // { value: "h", done: false }
iterator.next()  // { value: "i", done: false }
iterator.next()  // { value: undefined, done: true }

地点代码中,调用Symbol.iterator主意重返三个遍历器对象,在这些遍历器上得以调用next方法,完成对于字符串的遍历。

能够覆盖原生的Symbol.iterator主意,到达修改遍历器行为的目标。

var str = new String("hi");

[...str] // ["h", "i"]

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"

地点代码中,字符串str的Symbol.iterator办法被更动了,所以增加运算符(...)再次回到的值产生了bye,而字符串本人依然hi

Iterator接口与Generator函数

Symbol.iterator主意的最简易完结,依旧使用下一章要介绍的Generator函数。

var myIterable = {};

myIterable[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};
[...myIterable] // [1, 2, 3]

// 或者采用下面的简洁写法

let obj = {
  * [Symbol.iterator]() {
    yield 'hello';
    yield 'world';
  }
};

for (let x of obj) {
  console.log(x);
}
// hello
// world

地点代码中,Symbol.iterator办法大概不用计划任何代码,只要用yield命令给出每一步的重返值就可以。

Iterator接口与Generator函数

Symbol.iterator方式的最简便易行达成,依然选拔下一章要介绍的Generator函数。

var myIterable = {};

myIterable[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};
[...myIterable] // [1, 2, 3]

// 或者采用下面的简洁写法

let obj = {
  * [Symbol.iterator]() {
    yield 'hello';
    yield 'world';
  }
};

for (let x of obj) {
  console.log(x);
}
// hello
// world

地方代码中,Symbol.iterator方式差不离不用布署任何代码,只要用yield命令给出每一步的再次来到值就能够。

遍历器对象的return(),throw()

遍历器对象除了有着next办法,还足以具备return方法和throw措施。借使您本人写遍历器对象生成函数,那么next方法是必须配备的,return方法和throw主意是还是不是配备是可选的。

return艺术的施用场馆是,倘使for...of巡回提前退出(常常是因为出错,可能有break语句或continue言语),就会调用return方法。假设3个对象在做到遍历前,要求清理或自由能源,就能够配备return方法。

function readLinesSync(file) {
  return {
    next() {
      if (file.isAtEndOfFile()) {
        file.close();
        return { done: true };
      }
    },
    return() {
      file.close();
      return { done: true };
    },
  };
}

上边代码中,函数readLinesSync接受一个文件对象作为参数,重回四个遍历器对象,在那之中除了next措施,还配备了return方法。下边,大家让文件的遍历提前再次来到,那样就会接触施行return方法。

for (let line of readLinesSync(fileName)) {
  console.log(line);
  break;
}

注意,return主意必须重返五个目的,那是Generator规格决定的。

throw方法首假若合作Generator函数使用,一般的遍历器对象用不到那个方法。请参阅《Generator函数》壹章。

遍历器对象的return(),throw()

遍历器对象除了有着next措施,还足以具备return方法和throw情势。假使你和睦写遍历器对象生成函数,那么next措施是必须安顿的,return方法和throw格局是还是不是安排是可选的。

return办法的应用场合是,如若for...of巡回提前退出(经常是因为出错,可能有break语句或continue言语),就会调用return主意。如若一个目标在形成遍历前,须要清理或释放财富,就能够配备return方法。

function readLinesSync(file) {
  return {
    next() {
      if (file.isAtEndOfFile()) {
        file.close();
        return { done: true };
      }
    },
    return() {
      file.close();
      return { done: true };
    },
  };
}

上边代码中,函数readLinesSync收受2个文书对象作为参数,再次来到1个遍历器对象,当中除了next艺术,还配置了return办法。上面,大家让文件的遍历提前重回,那样就会接触推行return方法。

for (let line of readLinesSync(fileName)) {
  console.log(line);
  break;
}

注意,return艺术必须回到三个目标,那是Generator规格决定的。

throw主意首假设万分Generator函数使用,一般的遍历器对象用不到这么些办法。请参阅《Generator函数》一章。

for…of循环

ES6 借鉴 C++、Java、C# 和 Python
语言,引入了for...of巡回,作为遍历全部数据结构的集结的艺术。

3个数据结构只要安顿了Symbol.iterator天性,就被视为具有iterator接口,就足以用for...of巡回遍历它的成员。约等于说,for...of巡回之中调用的是数据结构的Symbol.iterator方法。

for...of循环能够利用的界定包含数组、Set 和 Map
结构、某个类似数组的目标(比方arguments目标、DOM NodeList
对象)、后文的 Generator 对象,以及字符串。

for…of循环

ES6 借鉴 C++、Java、C# 和 Python
语言,引入了for...of循环,作为遍历全数数据结构的联合的措施。

贰个数据结构只要陈设了Symbol.iterator品质,就被视为具备iterator接口,就足以用for...of巡回遍历它的积极分子。也正是说,for...of巡回之中调用的是数据结构的Symbol.iterator方法。

for...of循环能够动用的限制包罗数组、Set 和 Map
结构、有些类似数组的对象(比方arguments目标、DOM NodeList
对象)、后文的 Generator 对象,以及字符串。

数组

数组原生具有iterator接口(即暗中同意安排了Symbol.iterator属性),for...of循环本质上正是调用这些接口发生的遍历器,能够用下面包车型大巴代码申明。

const arr = ['red', 'green', 'blue'];

for(let v of arr) {
  console.log(v); // red green blue
}

const obj = {};
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);

for(let v of obj) {
  console.log(v); // red green blue
}

地点代码中,空对象obj配置了数组arrSymbol.iterator属性,结果objfor...of巡回,发生了与arr全然相同的结果。

for...of巡回能够代表数组实例的forEach方法。

const arr = ['red', 'green', 'blue'];

arr.forEach(function (element, index) {
  console.log(element); // red green blue
  console.log(index);   // 0 1 2
});

JavaScript原有的for...in巡回,只好获取对象的键名,不能够直接得到键值。ES陆提供for...of循环,允许遍历获得键值。

var arr = ['a', 'b', 'c', 'd'];

for (let a in arr) {
  console.log(a); // 0 1 2 3
}

for (let a of arr) {
  console.log(a); // a b c d
}

下面代码评释,for...in循环读取键名,for...of巡回读取键值。如若要透过for...of循环,获取数组的目录,能够正视数组实例的entries方法和keys措施,参见《数组的强大》章节。

for...of循环调用遍历器接口,数组的遍历器接口只回去具备数字索引的品质。那点跟for...in循环也不平等。

let arr = [3, 5, 7];
arr.foo = 'hello';

for (let i in arr) {
  console.log(i); // "0", "1", "2", "foo"
}

for (let i of arr) {
  console.log(i); //  "3", "5", "7"
}

下面代码中,for...of循环不会回去数组arrfoo属性。

数组

数组原生具有iterator接口(即默许安顿了Symbol.iterator属性),for...of巡回本质上正是调用那个接口发生的遍历器,能够用下边包车型地铁代码注脚。

const arr = ['red', 'green', 'blue'];

for(let v of arr) {
  console.log(v); // red green blue
}

const obj = {};
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);

for(let v of obj) {
  console.log(v); // red green blue
}

地方代码中,空对象obj布局了数组arrSymbol.iterator属性,结果objfor...of巡回,产生了与arr一心壹致的结果。

for...of巡回能够代替数组实例的forEach方法。

const arr = ['red', 'green', 'blue'];

arr.forEach(function (element, index) {
  console.log(element); // red green blue
  console.log(index);   // 0 1 2
});

JavaScript原有的for...in循环,只可以取得对象的键名,不可能平昔获得键值。ES六提供for...of巡回,允许遍历得到键值。

var arr = ['a', 'b', 'c', 'd'];

for (let a in arr) {
  console.log(a); // 0 1 2 3
}

for (let a of arr) {
  console.log(a); // a b c d
}

地点代码评释,for...in巡回读取键名,for...of巡回读取键值。如若要透过for...of循环,获取数组的目录,可以注重数组实例的entries方法和keys主意,参见《数组的恢弘》章节。

for...of循环调用遍历器接口,数组的遍历器接口只回去具备数字索引的天性。那一点跟for...in巡回也不同。

let arr = [3, 5, 7];
arr.foo = 'hello';

for (let i in arr) {
  console.log(i); // "0", "1", "2", "foo"
}

for (let i of arr) {
  console.log(i); //  "3", "5", "7"
}

上面代码中,for...of循环不会回到数组arrfoo属性。

Set和Map结构

Set 和 Map 结构也原生具备 Iterator 接口,能够向来行使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
成员的键名和键值。

let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
  console.log(pair);
}
// ['a', 1]
// ['b', 2]

for (let [key, value] of map) {
  console.log(key + ' : ' + value);
}
// a : 1
// b : 2

Set和Map结构

Set 和 Map 结构也原生具备 Iterator 接口,可以直接采纳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
结构遍历时,重返的是2个数组,该数组的七个分子分别为当前 Map
成员的键名和键值。

let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
  console.log(pair);
}
// ['a', 1]
// ['b', 2]

for (let [key, value] of map) {
  console.log(key + ' : ' + value);
}
// a : 1
// b : 2

计量生成的数据结构

有个别数据结构是在现成数据结构的基本功上,总计生成的。例如,ES陆的数组、Set、Map
都布置了以下多少个点子,调用后都回来遍历器对象。

  • entries() 重返1个遍历器对象,用来遍历[键名, 键值]结合的数组。对于数组,键名正是索引值;对于
    Set,键名与键值同样。Map 结构的 Iterator
    接口,默许正是调用entries方法。
  • keys() 重返三个遍历器对象,用来遍历全部的键名。
  • values() 重返一个遍历器对象,用来遍历全体的键值。

那八个主意调用后转移的遍历器对象,所遍历的都以计算生成的数据结构。

let arr = ['a', 'b', 'c'];
for (let pair of arr.entries()) {
  console.log(pair);
}
// [0, 'a']
// [1, 'b']
// [2, 'c']

测算生成的数据结构

稍稍数据结构是在存活数据结构的底蕴上,总结生成的。比方,ES陆的数组、Set、Map
都安插了以下七个主意,调用后都回到遍历器对象。

  • entries() 重临3个遍历器对象,用来遍历[键名, 键值]组合的数组。对于数组,键名便是索引值;对于
    Set,键名与键值一样。Map 结构的 Iterator
    接口,暗中同意正是调用entries方法。
  • keys() 重临一个遍历器对象,用来遍历全数的键名。
  • values() 重回二个遍历器对象,用来遍历全数的键值。

这五个主意调用后调换的遍历器对象,所遍历的都是测算生成的数据结构。

let arr = ['a', 'b', 'c'];
for (let pair of arr.entries()) {
  console.log(pair);
}
// [0, 'a']
// [1, 'b']
// [2, 'c']

类似数组的靶子

恍如数组的目的包罗一些类。下边是for...of循环用于字符串、DOM NodeList
对象、arguments目标的事例。

// 字符串
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");
}

// arguments对象
function printArgs() {
  for (let x of arguments) {
    console.log(x);
  }
}
printArgs('a', 'b');
// 'a'
// 'b'

对于字符串来讲,for...of循环还有1个特点,正是会不错识别3十三人 UTF-16字符。

for (let x of 'a\uD83D\uDC0A') {
  console.log(x);
}
// 'a'
// '\uD83D\uDC0A'

并不是全体类似数组的对象都持有 Iterator
接口,1个便捷的消除方式,就是行使Array.from艺术将其转为数组。

let arrayLike = { length: 2, 0: 'a', 1: 'b' };

// 报错
for (let x of arrayLike) {
  console.log(x);
}

// 正确
for (let x of Array.from(arrayLike)) {
  console.log(x);
}

接近数组的对象

类似数组的靶子包涵有个别类。下面是for...of巡回用于字符串、DOM NodeList
对象、arguments目的的例证。

// 字符串
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");
}

// arguments对象
function printArgs() {
  for (let x of arguments) {
    console.log(x);
  }
}
printArgs('a', 'b');
// 'a'
// 'b'

对此字符串来说,for...of巡回还有3个特征,就是会准确识别叁拾三个人 UTF-16字符。

for (let x of 'a\uD83D\uDC0A') {
  console.log(x);
}
// 'a'
// '\uD83D\uDC0A'

并不是独具类似数组的目的都抱有 Iterator
接口,一个方便人民群众的减轻办法,正是使用Array.from主意将其转为数组。

let arrayLike = { length: 2, 0: 'a', 1: 'b' };

// 报错
for (let x of arrayLike) {
  console.log(x);
}

// 正确
for (let x of Array.from(arrayLike)) {
  console.log(x);
}

对象

对于一般的靶子,for...of协会无法一向利用,会报错,必须配备了 Iterator
接口后才具应用。但是,那样意况下,for...in巡回依旧能够用来遍历键名。

let es6 = {
  edition: 6,
  committee: "TC39",
  standard: "ECMA-262"
};

for (let e in es6) {
  console.log(e);
}
// edition
// committee
// standard

for (let e of es6) {
  console.log(e);
}
// TypeError: es6 is not iterable

下面代码表示,对于常见的对象,for...in循环能够遍历键名,for...of循环会报错。

一种缓和办法是,使用Object.keys办法将对象的键名生成贰个数组,然后遍历这么些数组。

for (var key of Object.keys(someObject)) {
  console.log(key + ': ' + someObject[key]);
}

另三个办法是应用 Generator 函数将对象重新包装一下。

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布局不可能平昔运用,会报错,必须安顿了 Iterator
接口后手艺采取。可是,那样境况下,for...in循环照旧得以用来遍历键名。

let es6 = {
  edition: 6,
  committee: "TC39",
  standard: "ECMA-262"
};

for (let e in es6) {
  console.log(e);
}
// edition
// committee
// standard

for (let e of es6) {
  console.log(e);
}
// TypeError: es6 is not iterable

上边代码表示,对于一般的目的,for...in循环能够遍历键名,for...of循环会报错。

壹种减轻方法是,使用Object.keys格局将目的的键名生成三个数组,然后遍历那个数组。

for (var key of Object.keys(someObject)) {
  console.log(key + ': ' + someObject[key]);
}

另1个主意是行使 Generator 函数将目的重新打包一下。

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

与其它遍历语法的相比

以数组为例,JavaScript 提供七种遍历语法。最原始的写法正是for循环。

for (var index = 0; index < myArray.length; index++) {
  console.log(myArray[index]);
}

那种写法比较艰巨,因而数组提供放置的forEach方法。

myArray.forEach(function (value) {
  console.log(value);
});

那种写法的难题在于,不能够中途跳出forEach循环,break命令或return一声令下都不能见效。

for...in巡回能够遍历数组的键名。

for (var index in myArray) {
  console.log(myArray[index]);
}

for...in巡回有多少个缺陷。

  • 数组的键名是数字,不过for...in巡回是以字符串作为键名“0”、“一”、“2”等等。
  • for...in巡回不仅遍历数字键名,还会遍历手动增加的其余键,乃至包含原型链上的键。
  • 好几意况下,for...in循环会以随机顺序遍历键名。

总之,for...in循环重若是为遍历对象而布置的,不适用于遍历数组。

for...of循环相比较下面两种做法,有一些显眼的帮助和益处。

for (let value of myArray) {
  console.log(value);
}
  • 有着同for...in同样的简洁语法,不过未有for...in那一个缺点。
  • 今非昔比用于forEach措施,它可以与breakcontinuereturn万分使用。
  • 提供了遍历全数数据结构的集合操作接口。

下边是1个应用break语句,跳出for...of循环的例子。

for (var n of fibonacci) {
  if (n > 1000)
    break;
  console.log(n);
}

上边的例子,会输出斐波纳契数列小于等于一千的项。借使当前项高于1000,就会选取break话语跳出for...of循环。

与其余遍历语法的比较

以数组为例,JavaScript 提供二种遍历语法。最原始的写法正是for循环。

for (var index = 0; index < myArray.length; index++) {
  console.log(myArray[index]);
}

那种写法相比较费心,因而数组提供放置的forEach方法。

myArray.forEach(function (value) {
  console.log(value);
});

那种写法的难题在于,无法中途跳出forEach循环,break命令或return一声令下都无法行之有效。

for...in循环能够遍历数组的键名。

for (var index in myArray) {
  console.log(myArray[index]);
}

for...in巡回有多少个毛病。

  • 数组的键名是数字,不过for...in循环是以字符串作为键名“0”、“壹”、“二”等等。
  • for...in巡回不仅遍历数字键名,还会遍历手动增添的别的键,以致席卷原型链上的键。
  • 一些情状下,for...in循环会以自由顺序遍历键名。

总之,for...in循环首假设为遍历对象而计划的,不适用于遍历数组。

for...of循环比较上边两种做法,有一部明显了的亮点。

for (let value of myArray) {
  console.log(value);
}
  • 有着同for...in如出1辙的简洁语法,可是从未for...in那些缺点。
  • 今非昔比用于forEach办法,它可以与breakcontinuereturn协作使用。
  • 提供了遍历全部数据结构的会集操作接口。

下边是2个用到break语句,跳出for...of巡回的例证。

for (var n of fibonacci) {
  if (n > 1000)
    break;
  console.log(n);
}

上边的例证,会输出斐波纳契数列小于等于一千的项。如果当前项高于一千,就会接纳break言语跳出for...of循环。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website