ES6의 for...of 루프와 Iterable 객체 이해

青灯夜游
풀어 주다: 2020-11-12 18:12:35
앞으로
2226명이 탐색했습니다.

ES6의 for...of 루프와 Iterable 객체 이해

추천 튜토리얼: "JavaScript 비디오 튜토리얼"

이 글은 ES6의 for ... of 루프를 연구할 것입니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다. 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

기존 방식

과거에는 자바스크립트를 통해 반복하는 방법이 두 가지가 있었습니다.

First는 고전적인 for i 루프입니다. 이 루프를 사용하면 색인화 가능하고 length 속성이 있는 배열이나 모든 객체를 반복할 수 있습니다. 🎜
const foo = {
  [Symbol.iterator]: function() {
    return {
      next: function() {
      }
    }
  }
}

for(const [key, value] of foo) {
  console.log(key, value)
}
로그인 후 복사
🎜두 번째는 개체의 키/값 쌍을 반복하는 데 사용되는 for ... in 루프입니다. 🎜
for(const [key, value] of foo) {
                     ^
TypeError: Iterator result undefined is not an object
로그인 후 복사
로그인 후 복사
🎜for ... in 루프는 객체의 열거 가능한 각 속성을 반복하기 때문에 종종 참고 사항으로 간주됩니다. 여기에는 프로토타입 체인에 있는 상위 개체의 속성과 메서드에 할당된 모든 속성이 포함됩니다. 즉, 사람들이 예상하지 못할 일을 겪게 됩니다. for ... in을 사용한다는 것은 일반적으로 원치 않는 속성을 피하기 위해 루프 블록에 많은 보호 절이 있음을 의미합니다. 🎜🎜초기 JavaScript는 라이브러리를 통해 이 문제를 해결했습니다. 많은 JavaScript 라이브러리(예: Prototype.js, jQuery, lodash 등)에는 for 없이 이 작업을 수행할 수 있는 <code>each 또는 foreach와 같은 유틸리티 메서드나 함수가 있습니다. i 또는 for ... in은 객체와 배열을 반복합니다. 🎜🎜for ... of 루프는 타사 라이브러리 없이 이러한 문제 중 일부를 해결하려는 ES6의 방식입니다. 🎜

for … of

🎜for … of 루프🎜
next: function() {
    //...
    return {
        done: false,
        value: &#39;next value&#39;
    }
}
로그인 후 복사
로그인 후 복사
🎜반복 가능한객체를 순회합니다. 🎜🎜반복 가능한 객체는 @@ iterator 메소드를 정의하는 객체이며, @@iterator 메소드는 Iterator 프로토콜 또는 메서드가 생성기 함수입니다. 🎜🎜이 문장에서 많은 것을 이해해야 합니다: 🎜
  • Iterable object
  • @@iterator method ( < code>@@ 무슨 뜻인가요? )
  • Iterator 프로토콜(여기서 프로토콜은 무엇을 의미하나요?)
  • 잠깐, 반복 가능합니까? 그리고 반복자는 같은 것이 아닌가?
  • 게다가 제너레이터 함수가 도대체 ​​뭐죠?
🎜이러한 질문은 아래에서 하나씩 해결됩니다. 🎜

내장 Iterable

🎜우선 JavaScript 객체의 일부 내장 객체는 자연스럽게 반복될 수 있습니다. 예를 들어 가장 쉽게 생각할 수 있는 것은 배열 객체입니다. 다음 코드와 같이 for ... of 루프에서 배열을 사용할 수 있습니다. 🎜
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)
}
로그인 후 복사
🎜반복 가능한 객체를 반환하는 배열용 entries 메서드도 있습니다. 이 반복 가능 항목은 각 루프의 키와 값을 반환합니다. 예를 들어, 다음 코드: 🎜
$ node sample-program.js
2
4
6
8
10
로그인 후 복사
🎜는 다음을 출력합니다. 🎜rrreee🎜 entries 메소드는 다음 구문을 사용할 때 더 유용합니다. 🎜rrreee🎜는 for에서 두 개의Variables: 하나는 배열의 첫 번째 항목(값의 키 또는 인덱스)을 반환하기 위한 것이고, 다른 하나는 두 번째 항목(인덱스가 실제로 해당하는 값)을 위한 것입니다. 🎜🎜일반적인 자바스크립트 객체는 반복이 불가능합니다. 다음 코드를 실행하면 🎜rrreee🎜 오류가 발생합니다. 🎜rrreee🎜그러나 전역 Object 개체의 정적 entries 메서드는 허용됩니다. 일반 객체를 매개변수로 사용하고 Iterable 객체를 반환합니다. 다음과 같은 프로그램은 🎜rrreee🎜가 기대하는 결과를 얻을 수 있습니다. 🎜rrreee

자신만의 Iterable 만들기

🎜자신만의 Iterable 객체를 만들고 싶다면 다음과 같은 비용을 지출해야 합니다. 더 많은 시간. 앞서 말한 내용을 기억하실 것입니다. 🎜
반복 가능한 객체는 @@ iterator 메서드를 정의하는 객체이며, @@ iterator 메서드는 다음과 같은 구현을 반환합니다. 반복자 프로토콜의 객체 또는 메소드가 생성기 함수입니다.
🎜이를 이해하는 가장 쉬운 방법은 반복 가능한 객체를 단계별로 생성하는 것입니다. 먼저 @@iterator 메서드를 구현하는 객체가 필요합니다. @@ 표기법은 약간 오해의 소지가 있습니다. 우리가 정말로 원하는 것은 미리 정의된 Symbol.iterator 기호를 사용하여 메서드를 정의하는 것입니다. 🎜🎜반복자 메소드로 객체를 정의하고 반복하려고 하면: 🎜rrreee🎜새로운 오류가 발생합니다: 🎜rrreee🎜이것은 Symbol.iterator</를 호출하려고 한다는 것을 알려주는 자바스크립트입니다. code> 메서드이지만 호출 결과는<strong>객체가 아닙니다</strong>. 🎜🎜이 오류를 제거하려면 반복자 메서드를 사용하여 <strong>반복자 프로토콜</strong>을 구현하는 개체를 반환해야 합니다. 이는 반복기 메서드가 함수인 <code>next 키가 있는 객체를 반환해야 함을 의미합니다. 🎜rrreee🎜 위 코드를 실행하면 새로운 오류가 발생합니다. 🎜
for(const [key, value] of foo) {
                     ^
TypeError: Iterator result undefined is not an object
로그인 후 복사
로그인 후 복사

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

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

译文地址:https://segmentfault.com/a/1190000023924865

更多编程相关知识,请访问:编程学习网站!!

위 내용은 ES6의 for...of 루프와 Iterable 객체 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:segmentfault.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿