Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Erklärung von for...of-Schleifen und iterierbaren Objekten in ES6

Detaillierte Erklärung von for...of-Schleifen und iterierbaren Objekten in ES6

青灯夜游
青灯夜游nach vorne
2020-12-08 17:56:143022Durchsuche

Detaillierte Erklärung von for...of-Schleifen und iterierbaren Objekten in ES6

In diesem Artikel wird die ES6-for ... of-Schleife untersucht. for ... of 循环。

旧方法

在过去,有两种方法可以遍历 javascript。

首先是经典的 for i 循环,它使你可以遍历数组或可索引的且有 length 属性的任何对象。

for(i=0;i<things.length;i++) {
    var thing = things[i]
    /* ... */
}

其次是 for ... in 循环,用于循环一个对象的键/值对。

for(key in things) {
    if(!thing.hasOwnProperty(key)) { continue; }
    var thing = things[key]
    /* ... */
}

for ... in 循环通常被视作旁白,因为它循环了对象的每一个可枚举属性。这包括原型链中父对象的属性,以及被分配为方法的所以属性。换句话说,它遍历了一些人们可能想不到的东西。使用 for ... in 通常意味着循环块中有很多保护子句,以避免出现不需要的属性。

早期的 javascript 通过库解决了这个问题。许多 JavaScript库(例如:Prototype.js,jQuery,lodash 等)都有类似 eachforeach 这样的工具方法或函数,可让你无需 for ifor ... in 循环去遍历对象和数组。

for ... of 循环是 ES6 试图不用第三方库去解决其中一些问题的方式。

for … of

for ... of 循环

for(const thing of things) {
    /* ... */
}

它将遍历一个可迭代(iterable)对象。

可迭代对象是定义了 @@ iterator 方法的对象,而且 @@iterator 方法返回一个实现了迭代器协议的对象,或者该方法是生成器函数。

在这句话中你需要理解很多东西:

  • 可迭代的对象
  • @@iterator方法( @@是什么意思?)
  • 迭代器协议(这里的协议是什么意思?)
  • 等等,迭代(iterable)和迭代器(iterator)不是一回事?
  • 另外,生成器函数又是什么鬼?

下面逐个解决这些疑问。

内置 Iterable

首先,javascript 对象中的一些内置对象天然的可以迭代,比如最容易想到的就是数组对象。可以像下面的代码中一样在 for ... of 循环中使用数组:

const foo = [
&#39;apples&#39;,&#39;oranges&#39;,&#39;pears&#39;
]

for(const thing of foo) {
  console.log(thing)
}

输出结果是数组中的所有元素。

apples
oranges
pears

还有数组的 entries 方法,它返回一个可迭代对象。这个可迭代对象在每次循环中返回键和值。例如下面的代码:

const foo = [
&#39;apples&#39;,&#39;oranges&#39;,&#39;pears&#39;
]

for(const thing of foo.entries()) {
  console.log(thing)
}

将输出以下内容

[ 0, &#39;apples&#39; ]
[ 1, &#39;oranges&#39; ]
[ 2, &#39;pears&#39; ]

当用下面的语法时,entries 方法会更有用

const foo = [
    &#39;apples&#39;,&#39;oranges&#39;,&#39;pears&#39;
]

for(const [key, value] of foo.entries()) {
  console.log(key,&#39;:&#39;,value)
}

在 for 循环中声明了两个变量:一个用于返回数组的第一项(值的键或索引),另一个用于第二项(该索引实际对应的值)。

一个普通的 javascript 对象是不可迭代的。如果你执行下面这段代码:

// 无法正常执行
const foo = {
  &#39;apples&#39;:&#39;oranges&#39;,
  &#39;pears&#39;:&#39;prunes&#39;
}

for(const [key, value] of foo) {
  console.log(key,&#39;:&#39;,value)
}

会得到一个错误

$ node test.js
/path/to/test.js:6
for(const [key, value] of foo) {
TypeError: foo is not iterable

然而全局 Object 对象的静态 entries 方法接受一个普通对象作为参数,并返回一个可迭代对象。就像这样的程序:

const foo = {
  &#39;apples&#39;:&#39;oranges&#39;,
  &#39;pears&#39;:&#39;prunes&#39;
}

for(const [key, value] of Object.entries(foo)) {
  console.log(key,&#39;:&#39;,value)
}

能够得到你期望的输出:

$ node test.js
apples : oranges
pears : prunes

创建自己的 Iterable

如果你想创建自己的可迭代对象,则需要花费更多的时间。你会记得前面说过:

可迭代对象是定义了 @@ iterator 方法的对象,而且 @@iterator 方法返回一个实现了迭代器协议的对象,或者该方法是生成器函数。

搞懂这些内容的最简单方法就是一步一步的去创建可迭代对象。首先,我们需要一个实现 @@iterator 方法的对象。  @@ 表示法有点误导性,我们真正??要做的是用预定义的 Symbol.iterator 符号定义方法。

如果用迭代器方法定义对象并尝试遍历:

const foo = {
  [Symbol.iterator]: function() {
  }
}

for(const [key, value] of foo) {
  console.log(key, value)
}

得到一个新错误:

for(const [key, value] of foo) {
                          ^
TypeError: Result of the Symbol.iterator method is not an object

这是 javascript 告诉我们它在试图调用 Symbol.iterator 方法,但是调用的结果不是对象。

为了消除这个错误,需要用迭代器方法来返回实现了迭代器协议的对象。这意味着迭代器方法需要返回一个有 next 键的对象,而 next 键是一个函数。

const foo = {
  [Symbol.iterator]: function() {
    return {
      next: function() {
      }
    }
  }
}

for(const [key, value] of foo) {
  console.log(key, value)
}

如果运行上面的代码,则会出现新错误。

for(const [key, value] of foo) {
                     ^
TypeError: Iterator result undefined is not an object

这次 javascript 告诉我们它试图调用 Symbol.iterator 方法,而该对象的确是一个对象,并且实现了 next 方法,但是 next

Alter Weg

In der Vergangenheit gab es zwei Möglichkeiten, Javascript zu durchlaufen. 🎜🎜Zuerst gibt es die klassische for i-Schleife, die es Ihnen ermöglicht, über ein Array oder ein beliebiges Objekt zu iterieren, das indizierbar ist und über eine length-Eigenschaft verfügt. 🎜
next: function() {
    //...
    return {
        done: false,
        value: &#39;next value&#39;
    }
}
🎜Die zweite ist die for ... in-Schleife, die zum Durchlaufen der Schlüssel/Wert-Paare eines Objekts verwendet wird. 🎜
class First20Evens {
  constructor() {
    this.currentValue = 0
  }

  [Symbol.iterator]() {
    return {
      next: (function() {
        this.currentValue+=2
        if(this.currentValue > 20) {
          return {done:true}
        }
        return {
          value:this.currentValue
        }
      }).bind(this)
    }
  }
}

const foo = new First20Evens;
for(const value of foo) {
  console.log(value)
}
🎜for ... in Die Schleife wird oft als Randbemerkung betrachtet, da sie Jede aufzählbare Eigenschaft des Objekts. Dazu gehören Eigenschaften des übergeordneten Objekts in der Prototypenkette sowie alle Eigenschaften, die Methoden zugewiesen sind. Mit anderen Worten: Es passieren einige Dinge, mit denen man vielleicht nicht gerechnet hätte. Die Verwendung von for ... in bedeutet normalerweise, dass im Schleifenblock viele Schutzklauseln vorhanden sind, um unerwünschte Attribute zu vermeiden. 🎜🎜Frühes JavaScript löste dieses Problem durch Bibliotheken. Viele JavaScript-Bibliotheken (z. B. Prototype.js, jQuery, lodash usw.) verfügen über Hilfsmethoden oder -funktionen wie each oder foreach, mit denen Sie dies ohne for tun können i oder for ... in durchläuft Objekte und Arrays. 🎜🎜for ... of-Schleifen sind ES6s Methode, einige dieser Problemeohne Bibliotheken von Drittanbietern zu lösen. 🎜

for … of

🎜for … of Schleife🎜
class First20Evens {
  constructor() {
    this.currentValue = 0
  }

  [Symbol.iterator]() {
    return function*() {
      for(let i=1;i<=10;i++) {
        if(i % 2 === 0) {
          yield i
        }
      }
    }()
  }
}

const foo = new First20Evens;
for(const item of foo) {
  console.log(item)
}
🎜Es wird ein iterierbaresObjekt durchlaufen. 🎜🎜Ein iterierbares Objekt ist ein Objekt, das die Methode @@ iterator definiert, und die Methode @@iterator gibt ein Objekt zurück, das das Iterator-Protokollimplementiert > oder die Methode ist eine Generatorfunktion. 🎜🎜Sie müssen viele Dinge in diesem Satz verstehen: 🎜
  • Iterable object
  • @@iterator method ( @@ Was bedeutet das? )
  • Iterator Protokoll (Was bedeutet das Protokoll hier?)
  • Warten Sie, sind iterierbar und Iterator nicht dasselbe?
  • Außerdem: Was zum Teufel ist eine Generatorfunktion?
🎜Diese Fragen werden im Folgenden einzeln beantwortet. 🎜

Integriert iterierbar

🎜Zuallererst können einige integrierte Objekte in JavaScript-Objekten auf natürliche Weise iteriert werden. Am einfachsten sind beispielsweise Array-Objekte. Arrays können in einer for ... of-Schleife wie im folgenden Code verwendet werden: 🎜
$ node sample-program.js
2
4
6
8
10
🎜Die Ausgabe sind alle Elemente im Array. 🎜rrreee🎜Es gibt auch eine entries-Methode für Arrays, die ein iterierbares Objekt zurückgibt. Diese Iteration gibt den Schlüssel und den Wert für jede Schleife zurück. Beispielsweise gibt der folgende Code: 🎜rrreee🎜 Folgendes aus: 🎜rrreee🎜Die Methode entries ist nützlicher, wenn die folgende Syntax verwendet wird: 🎜rrreee🎜 deklariert zweiim for Schleife stark>Variablen: eine für die Rückgabe des ersten Elements des Arrays (der Schlüssel oder Index des Werts) und eine für das zweite Element (der Wert, dem der Index tatsächlich entspricht). 🎜🎜Ein normales Javascript-Objekt ist nicht iterierbar. Wenn Sie den folgenden Code ausführen: 🎜rrreee🎜 erhalten Sie eine Fehlermeldung 🎜rrreee🎜Allerdings akzeptiert die statische entries-Methode des globalen Object-Objekts ein normales Objekt als Parameter und gibt einIterable-Objekt zurück. Ein Programm wie dieses: 🎜rrreee🎜 kann die Ausgabe erhalten, die Sie erwarten: 🎜rrreee

Erstellen Sie Ihr eigenes Iterable

🎜Wenn Sie Ihr eigenes Iterable-Objekt erstellen möchten, müssen Sie ausgeben Mehr Zeit. Sie werden sich erinnern, was ich zuvor gesagt habe: 🎜
Ein iterierbares Objekt ist ein Objekt, das die Methode @@ iterator definiert, und die Methode @@iterator gibt eine Implementierung zurück, die Ein Objekt des Iteratorprotokolls oder der Methode ist eine Generatorfunktion.
🎜Der einfachste Weg, dies zu verstehen, besteht darin, Schritt für Schritt ein iterierbares Objekt zu erstellen. Zuerst benötigen wir ein Objekt, das die Methode @@iterator implementiert. Die @@-Notation ist etwas irreführend. Eigentlich wollen wir die Methode mithilfe des vordefinierten Symbols Symbol.iterator definieren. 🎜🎜Wenn Sie ein Objekt mit einer Iteratormethode definieren und versuchen, darüber zu iterieren: 🎜rrreee🎜Sie erhalten eine neue Fehlermeldung: 🎜rrreee🎜Dies ist Javascript, das uns mitteilt, dass es versucht, den Symbol.iterator aufzurufen. code>-Methode, aber das Ergebnis des Aufrufs ist kein Objekt. 🎜🎜Um diesen Fehler zu beheben, ist es notwendig, die Iterator-Methode zu verwenden, um ein Objekt zurückzugeben, das das <strong>Iterator-Protokoll</strong> implementiert. Das bedeutet, dass die Iteratormethode ein Objekt mit einem <code>next-Schlüssel zurückgeben muss, bei dem es sich um eine Funktion handelt. 🎜rrreee🎜 Wenn Sie den obigen Code ausführen, wird ein neuer Fehler angezeigt. 🎜rrreee🎜Dieses Mal teilt uns Javascript mit, dass es versucht, die Methode Symbol.iterator aufzurufen, und dass das Objekt tatsächlich ein Objekt ist und den next-Methode, aber der Rückgabewert von <code>next ist nicht das von Javascript erwartete Objekt. 🎜

next 函数需要返回有特定格式的对象——有 valuedone 这两个键。

next: function() {
    //...
    return {
        done: false,
        value: &#39;next value&#39;
    }
}

done 键是可选的。如果值为 true(表示迭代器已完成迭代),则说明迭代已结束。

如果 donefalse 或不存在,则需要 value 键。 value 键是通过循环此应该返回的值。

所以在代码中放入另一个程序,它带有一个简单的迭代器,该迭代器返回前十个偶数。

class First20Evens {
  constructor() {
    this.currentValue = 0
  }

  [Symbol.iterator]() {
    return {
      next: (function() {
        this.currentValue+=2
        if(this.currentValue > 20) {
          return {done:true}
        }
        return {
          value:this.currentValue
        }
      }).bind(this)
    }
  }
}

const foo = new First20Evens;
for(const value of foo) {
  console.log(value)
}

生成器

手动去构建实现迭代器协议的对象不是唯一的选择。生成器对象(由生成器函数返回)也实现了迭代器协议。上面的例子用生成器构建的话看起来像这样:

class First20Evens {
  constructor() {
    this.currentValue = 0
  }

  [Symbol.iterator]() {
    return function*() {
      for(let i=1;i<=10;i++) {
        if(i % 2 === 0) {
          yield i
        }
      }
    }()
  }
}

const foo = new First20Evens;
for(const item of foo) {
  console.log(item)
}

本文不会过多地介绍生成器,如果你需要入门的话可以看这篇文章。今天的重要收获是,我们可以使自己的 Symbol.iterator 方法返回一个生成器对象,并且该生成器对象能够在 for ... of 循环中“正常工作”。 “正常工作”是指循环能够持续的在生成器上调用 next,直到生成器停止 yield 值为止。

$ node sample-program.js
2
4
6
8
10

英文原文地址:https://alanstorm.com/es6s-many-for-loops-and-iterable-objects/

作者:Alan Storm

更多编程相关知识,请访问:编程视频!!

Das obige ist der detaillierte Inhalt vonDetaillierte Erklärung von for...of-Schleifen und iterierbaren Objekten in ES6. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen