es6 语法 (iterator和for…of循环)

By admin in 4858美高梅 on 2019年4月22日

Iterator遍历器

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

作用:

  • 为种种数据结构,提供八个联合的、简便的造访接口
  • 使得数据结构的分子能够按某种次序排列
  • ES六开立了一种新的遍历命令for...of循环,Iterator接口首要供for...of消费

Iterator的遍历进程:

(壹)创设1个指南针对象,指向当前数据结构的胚胎地点。也等于说,遍历器对象本质上,正是三个指南针对象。

(二)第一遍调用指针对象的next措施,能够将指针指向数据结构的率先个分子。

(3)第3遍调用指针对象的next方法,指针就针对数据结构的第三个分子。

(四)不断调用指针对象的next情势,直到它指向数据结构的收尾地方。

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

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

Iterator遍历器

遍历器(Iterator)正是这样一种体制。它是一种接口,为各个区别的数据结构提供联合的走访机制。任何数据结构只要陈设Iterator接口,就足以做到遍历操作(即依次拍卖该数据结构的具有成员)。

作用:

  • 为各个数据结构,提供1个统1的、简便的拜会接口
  • 使得数据结构的分子能够按某种次序排列
  • ES陆创设了1种新的遍历命令for...of巡回,Iterator接口首要供for...of消费

Iterator的遍历进度:

(一)创立三个指针对象,指向当前数据结构的发端地方。也正是说,遍历器对象本质上,便是一个指针对象。

(二)第三回调用指针对象的next办法,能够将指针指向数据结构的第1个分子。

(3)第三遍调用指针对象的next办法,指针就对准数据结构的第二个成员。

(四)不断调用指针对象的next方法,直到它指向数据结构的截至地方。

在ES陆中,有三类数据结构原生具备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),ES6又增多了Map和Set。那样就有了多种多少集合,用户还足以组成使用它们,定义自个儿的数据结构,比方数组的积极分子是Map,Map的成员是目的。那样就须求一种统壹的接口机制,来拍卖全体不一致的数据结构。

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

Iterator的效果有两个:壹是为各个数据结构,提供三个联结的、简便的访问接口;2是驱动数据结构的积极分子可以按某种次序排列;三是ES陆成立了1种新的遍历命令for...of循环,Iterator接口首要供for...of消费。

Iterator的遍历进度是那般的。

(一)创造二个指针对象,指向当前数据结构的发端地点。也正是说,遍历器对象本质上,就是一个指针对象。

(贰)首回调用指针对象的next格局,能够将指针指向数据结构的第几个成员。

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

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

每二次调用next措施,都会回来数据结构的当下成员的音信。具体来说,正是回去2个分包valuedone八个属性的目的。个中,value性格是当下成员的值,done品质是二个布尔值,表示遍历是或不是结束。

上面是1个模仿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};
    }
  };
}

地点代码定义了3个makeIterator函数,它是一个遍历器生成函数,功能正是回去八个遍历器对象。对数组['a', 'b']实行那些函数,就会回来该数组的遍历器对象(即指针对象)it

指南针对象的next艺术,用来移动指针。开头时,指针指向数组的上马地点。然后,每便调用next措施,指针就会指向数组的下一个分子。第3遍调用,指向a;第1遍调用,指向b

next措施重返五个目的,表示近期多少成员的消息。那一个目的具有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只是把接口标准加到数据结构之上,所以,遍历器与它所遍历的百般数据结构,实际上是分其他,完全能够写出从未对号入座数据结构的遍历器对象,或然说用遍历器对象模拟出数据结构。上边是3个最佳运转的遍历器对象的例子。

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,
}

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),ES6又增多了Map和Set。那样就有了多种多少集结,用户还能够构成使用它们,定义自个儿的数据结构,比方数组的成员是Map,Map的成员是目的。那样就须要1种统一的接口机制,来拍卖全数不一致的数据结构。

遍历器(Iterator)正是那般1种机制。它是1种接口,为各类不相同的数据结构提供统1的走访机制。任何数据结构只要安顿Iterator接口,就足以产生遍历操作(即依次拍卖该数据结构的具有成员)。

Iterator的机能有多少个:1是为各个数据结构,提供多个集合的、简便的造访接口;二是驱动数据结构的分子能够按某种次序排列;3是ES陆成立了壹种新的遍历命令for...of循环,Iterator接口重要供for...of消费。

Iterator的遍历过程是那样的。

(一)创设一个指南针对象,指向当前数据结构的序曲地点。也便是说,遍历器对象本质上,就是二个指南针对象。

(二)第壹回调用指针对象的next方法,能够将指针指向数据结构的率先个分子。

(3)第一回调用指针对象的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函数,它是一个遍历器生成函数,作用就是回去三个遍历器对象。对数组['a', 'b']施行这一个函数,就会回来该数组的遍历器对象(即指针对象)it

指南针对象的next措施,用来移动指针。开始时,指针指向数组的初始地方。然后,每趟调用next办法,指针就会指向数组的下二个成员。第二遍调用,指向a;第1遍调用,指向b

next办法再次来到四个目的,表示近来数量成员的信息。那个目标具备valuedone四个个性,value性格重回当前地方的成员,done品质是3个布尔值,表示遍历是或不是停止,就是或不是还有需求再二遍调用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只是把接口标准加到数据结构之上,所以,遍历器与它所遍历的不行数据结构,实际上是分离的,完全能够写出从未对应数据结构的遍历器对象,也许说用遍历器对象模拟出数据结构。上边是3个极致运转的遍历器对象的事例。

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品质的数据结构,就称为陈设了遍历器接口。调用这些接口,就会回去2个遍历器对象。

如若利用TypeScript的写法,遍历器接口(Iterable)、指针对象(Iterator)和next方法再次来到值的尺度能够描述如下。

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

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

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

数据结构的暗中认可Iterator接口

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

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

ES六规定,默许的Iterator接口安插在数据结构的Symbol.iterator属性,恐怕说,1个数据结构只要具备Symbol.iterator性格,就能够感觉是“可遍历的”(iterable)。Symbol.iterator质量自身是一个函数,就是当下数据结构暗许的遍历器生成函数。实施那几个函数,就会回来一个遍历器。至于属性名Symbol.iterator,它是2个表明式,再次回到Symbol对象的iterator天性,那是二个预订义好的、类型为Symbol的例外值,所以要放在方括号内。(参见Symbol壹章)。

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

上边代码中,对象obj是可遍历的(iterable),因为拥有Symbol.iterator天性。实践那个特性,会回来1个遍历器对象。该目的的有史以来特征就是具有nextes6 语法 (iterator和for…of循环)。格局。每趟调用next办法,都会再次来到三个意味当前成员的音信目的,具有valuedone多少个性子。

在ES陆中,有3类数据结构原生具有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种线性管理,对于别的非线性的数据结构,布署遍历器接口,就等于陈设一种线性调换。可是,严厉地说,对象布署遍历器接口并不是很要求,因为此时对象实际被作为Map结构选用,ES伍从未有过Map结构,而ES6原生提供了。

三个对象假诺要有可被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};
    }
    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主意,在回去三个值的还要,自动将内部指针移到下三个实例。

下边是另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接口的目的,就是为全数数据结构,提供了一种统一的走访机制,即for...of巡回(详见下文)。当使用for...of巡回遍历某种数据结构时,该循环会自动去追寻Iterator接口。

一种数据结构只要计划了Iterator接口,我们就称那种数据结构是”可遍历的“(iterable)。

ES六鲜明,暗中认可的Iterator接口安顿在数据结构的Symbol.iterator本性,大概说,一个数据结构只要具有Symbol.iterator个性,就能够以为是“可遍历的”(iterable)。Symbol.iterator属性本人是叁个函数,正是日前数据结构私下认可的遍历器生成函数。推行那些函数,就会重回三个遍历器。至于属性名Symbol.iterator,它是3个表明式,再次回到Symbol对象的iterator特性,这是七个约定义好的、类型为Symbol的奇特值,所以要放在方括号内。(参见Symbol1章)。

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类,对于这3类数据结构,不用自个儿写遍历器生成函数,for...of循环会自动遍历它们。除却,别的数据结构(首若是目的)的Iterator接口,都亟需团结在Symbol.iterator属性上边陈设,那样才会被for...of循环遍历。

对象(Object)之所以没有暗中同意安顿Iterator接口,是因为对象的哪些属性先遍历,哪个属性后遍历是不鲜明的,必要开荒者手动内定。本质上,遍历器是一种线性管理,对于其余非线性的数据结构,安插遍历器接口,就卓越计划一种线性转变。可是,严峻地说,对象安排遍历器接口并不是很要求,因为此时对象实际被当作Map结构选取,ES伍未有Map结构,而ES陆原生提供了。

三个目的假如要有可被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);
}

上面代码是一个类安插Iterator接口的写法。Symbol.iterator质量对应3个函数,实行后重临当前目的的遍历器对象。

上边是由此遍历器达成指针结构的例子。

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方法对应的不是遍历器生成函数,由此报错。

4858美高梅 ,有了遍历器接口,数据结构就能够用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接口的场子

有1对场地会暗中同意调用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'];

(二)扩张运算符

恢宏运算符(…)也会调用私下认可的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*背后跟的是2个可遍历的构造,它会调用该组织的遍历器接口。

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巡回,还有多少个其他场所。

(一)解构赋值

对数组和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接口。

实际,这提供了一种便利机制,可以将别的陈设了Iterator接口的数据结构,转为数组。也正是说,只要有个别数据结构安排了Iterator接口,就能够对它选拔扩张运算符,将其转为数组。

let arr = [...iterable];

(3)yield*

yield*末端跟的是3个可遍历的布局,它会调用该组织的遍历器接口。

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接口

字符串是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格局的最简便完成,依旧利用下1章要介绍的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函数》1章。

遍历器对象的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接受二个文件对象作为参数,再次回到三个遍历器对象,个中除了next方法,还陈设了return措施。下面,我们让文件的遍历提前重临,那样就会接触实施return方法。

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

注意,return方法必须回到多少个目的,那是Generator规格决定的。

throw艺术重假若协作Generator函数使用,一般的遍历器对象用不到这么些主意。请参阅《Generator函数》1章。

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 对象,以及字符串。

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
结构遍历时,再次来到的是1个数组,该数组的几个分子分别为目前 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
结构遍历时,重返的是八个数组,该数组的七个分子分别为当下 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() 重返三个遍历器对象,用来遍历[键名, 键值]组合的数组。对于数组,键名正是索引值;对于
    Set,键名与键值一样。Map 结构的 Iterator
    接口,私下认可就是调用entries方法。
  • keys() 重返3个遍历器对象,用来遍历全体的键名。
  • 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() 再次回到三个遍历器对象,用来遍历[键名, 键值]组合的数组。对于数组,键名就是索引值;对于
    Set,键名与键值同样。Map 结构的 Iterator
    接口,暗许正是调用entries方法。
  • keys() 重返1个遍历器对象,用来遍历全体的键名。
  • 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巡回还有三个特点,即是会精确识别3十五人 UTF-1陆字符。

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巡回用于字符串、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循环还有二个特征,正是会不错识别三十六人 UTF-1陆字符。

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循环会报错。

1种缓慢解决方法是,使用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循环会报错。

1种缓和方法是,使用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

与任何遍历语法的相比较

以数组为例,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”、“1”、“二”等等。
  • for...in巡回不仅遍历数字键名,还会遍历手动增加的别的键,以致席卷原型链上的键。
  • 好几情形下,for...in循环会以随机顺序遍历键名。

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

for...of巡回比较上边两种做法,有一对醒目标亮点。

for (let value of myArray) {
  console.log(value);
}
  • 有着同for...in平等的精简语法,不过并未有for...in那多少个缺点。
  • 分化用于forEach主意,它能够与breakcontinuereturn相称使用。
  • 提供了遍历全体数据结构的合并操作接口。

上边是三个利用break语句,跳出for...of循环的例子。

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

地方的例子,会输出斐波纳契数列小于等于1000的项。借使当前项高于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循环相比较上边三种做法,有1部显然了的独到之处。

for (let value of myArray) {
  console.log(value);
}
  • 有着同for...in一样的简练语法,然则尚未for...in那三个缺点。
  • 今非昔比用于forEach方法,它能够与breakcontinuereturn万分使用。
  • 提供了遍历全数数据结构的联合操作接口。

上面是一个采用break语句,跳出for...of循环的事例。

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

地点的例证,会输出斐波纳契数列小于等于一千的项。假如当前项高于一千,就会采纳break话语跳出for...of循环。

发表评论

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

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有