반복 가능한 객체는 배열의 일반화입니다. 이는 for..of
루프에서 어떤 객체든 사용할 수 있게 만드는 개념입니다.
물론 배열은 반복 가능합니다. 그러나 반복 가능한 다른 내장 객체도 많이 있습니다. 예를 들어 문자열도 반복 가능합니다.
객체가 기술적으로 배열은 아니지만 무언가의 컬렉션(목록, 집합)을 나타내는 경우 for..of
는 이를 반복하는 훌륭한 구문이므로 작동하게 만드는 방법을 살펴보겠습니다.
우리는 반복 가능한 개념을 우리 자신의 것으로 만들어 쉽게 이해할 수 있습니다.
예를 들어, 배열은 아니지만 for..of
에 적합해 보이는 객체가 있습니다.
숫자 간격을 나타내는 range
객체와 같습니다.
범위 = { 에서: 1, ~: 5 }; // 우리는 for..of가 작동하기를 원합니다: // for(let num of range) ... num=1,2,3,4,5
range
객체를 반복 가능하게 만들려면(따라서 for..of
작업을 허용하려면) Symbol.iterator
(이를 위한 특수 내장 기호)라는 이름의 객체에 메서드를 추가해야 합니다.
for..of
시작되면 해당 메서드를 한 번 호출합니다(또는 찾을 수 없으면 오류가 발생합니다). 메소드는 반복자 ( next
메소드가 있는 객체)를 반환해야 합니다.
앞으로 for..of
반환된 객체에만 작동합니다.
for..of
다음 값을 원하면 해당 객체에 대해 next()
호출합니다.
next()
의 결과는 {done: Boolean, value: any}
형식이어야 합니다. 여기서 done=true
루프가 완료되었음을 의미하고, 그렇지 않으면 value
다음 값입니다.
비고가 포함된 range
의 전체 구현은 다음과 같습니다.
범위 = { 에서: 1, ~: 5 }; // 1. for..of를 호출하면 처음에는 이것을 호출합니다. 범위[Symbol.iterator] = 함수() { // ...iterator 객체를 반환합니다: // 2. 이후 for..of는 아래 반복자 객체에만 작동하여 다음 값을 요청합니다. 반품 { 현재: this.from, 마지막: this.to, // 3. next()는 for..of 루프에 의해 반복될 때마다 호출됩니다. 다음() { // 4. 값을 객체 {done:.., value :...}로 반환해야 합니다. if (this.current <= this.last) { return { 완료: false, 값: this.current++ }; } 또 다른 { return { 완료: true }; } } }; }; // 이제 작동합니다! for(범위 수를 지정) { 경고(숫자); // 1, 그다음 2, 3, 4, 5 }
iterable의 핵심 기능인 관심사 분리에 유의하세요.
range
자체에는 next()
메서드가 없습니다.
대신, range[Symbol.iterator]()
호출을 통해 소위 "반복자"라는 또 다른 객체가 생성되고 해당 객체의 next()
반복에 대한 값을 생성합니다.
따라서 반복자 객체는 반복되는 객체와 별개입니다.
기술적으로는 이들을 병합하고 range
자체를 반복자로 사용하여 코드를 더 단순하게 만들 수 있습니다.
이와 같이:
범위 = { 에서: 1, 에: 5, [Symbol.iterator]() { this.current = this.from; 이거 돌려줘; }, 다음() { if (this.current <= this.to) { return { 완료: false, 값: this.current++ }; } 또 다른 { return { 완료: true }; } } }; for(범위 수를 지정) { 경고(숫자); // 1, 그다음 2, 3, 4, 5 }
이제 range[Symbol.iterator]()
range
객체 자체를 반환합니다. 이 객체에는 필요한 next()
메서드가 있으며 this.current
에서 현재 반복 진행 상황을 기억합니다. 더 짧나요? 예. 때로는 그것도 괜찮습니다.
단점은 이제 객체에 대해 두 개의 for..of
루프를 동시에 실행하는 것이 불가능하다는 것입니다. 반복자(객체 자체)가 하나만 있기 때문에 반복 상태를 공유하게 됩니다. 그러나 두 개의 병렬 for-of는 비동기 시나리오에서도 드문 일입니다.
무한 반복자
무한 반복자도 가능합니다. 예를 들어 range.to = Infinity
의 경우 range
무한대가 됩니다. 또는 무한한 의사 난수 시퀀스를 생성하는 반복 가능한 개체를 만들 수도 있습니다. 또한 유용할 수 있습니다.
next
에는 제한이 없으며 점점 더 많은 값을 반환할 수 있습니다. 이는 정상입니다.
물론, 그러한 반복 가능 항목에 대한 for..of
루프는 끝이 없습니다. 하지만 언제든지 break
사용하여 이를 중지할 수 있습니다.
배열과 문자열은 가장 널리 사용되는 내장 반복 가능 항목입니다.
문자열의 경우 for..of
해당 문자를 반복합니다.
for (let char of "test") { // 4번 트리거됩니다: 각 캐릭터에 대해 한 번씩 경고( 문자 ); // t, 그 다음 e, 그 다음 s, 그 다음 t }
그리고 대리 쌍에서도 올바르게 작동합니다!
str = '??'; for (str의 문자를 보자) { 경고( 문자 ); // ?, 그런 다음 ? }
더 깊은 이해를 위해 반복자를 명시적으로 사용하는 방법을 살펴보겠습니다.
for..of
와 똑같은 방식으로 문자열을 반복하지만 직접 호출을 사용합니다. 이 코드는 문자열 반복자를 생성하고 "수동으로" 값을 가져옵니다.
str = "안녕하세요"; //와 동일합니다 // for(str의 char를 지정) Alert(char); iterator = str[Symbol.iterator](); 동안 (참) { 결과 = iterator.next(); if (result.done) 중단; 경고(결과.값); // 문자를 하나씩 출력합니다. }
이는 거의 필요하지 않지만 for..of
보다 프로세스를 더 잘 제어할 수 있습니다. 예를 들어, 반복 프로세스를 분할할 수 있습니다. 약간 반복한 다음 중지하고 다른 작업을 수행한 다음 나중에 다시 시작합니다.
두 가지 공식 용어는 비슷해 보이지만 매우 다릅니다. 혼란을 피하기 위해 잘 이해하시기 바랍니다.
Iterables 는 위에서 설명한 대로 Symbol.iterator
메서드를 구현하는 객체입니다.
유사 배열은 인덱스와 length
갖는 객체이므로 배열처럼 보입니다.
브라우저나 다른 환경에서 실용적인 작업을 위해 JavaScript를 사용할 때 반복 가능하거나 배열과 유사한 객체, 또는 둘 다인 객체를 만날 수 있습니다.
예를 들어, 문자열은 반복 가능( for..of
작동)하고 유사 배열(숫자 인덱스와 length
가짐)입니다.
그러나 iterable은 배열과 유사하지 않을 수 있습니다. 그리고 그 반대의 경우에도 배열과 유사한 것은 반복 가능하지 않을 수 있습니다.
예를 들어, 위 예제의 range
반복 가능하지만 인덱스 속성과 length
없기 때문에 배열과 유사하지는 않습니다.
배열과 비슷하지만 반복할 수 없는 객체는 다음과 같습니다.
let arrayLike = { // 인덱스와 길이를 가짐 => 유사 배열 0: "안녕하세요", 1: "세계", 길이: 2 }; // 오류(Symbol.iterator 없음) for(arrayLike의 항목 허용) {}
iterable과 유사 배열은 모두 일반적으로 array가 아니며 push
, pop
등이 없습니다. 그러한 객체가 있고 이를 배열처럼 사용하려는 경우 다소 불편합니다. 예를 들어 배열 방법을 사용하여 range
작업을 하고 싶습니다. 그것을 달성하는 방법은 무엇입니까?
반복 가능하거나 배열과 유사한 값을 가져와서 "실제" Array
만드는 범용 메서드 Array.from이 있습니다. 그런 다음 배열 메서드를 호출할 수 있습니다.
예를 들어:
arrayLike = {하자 0: "안녕하세요", 1: "세계", 길이: 2 }; arr = Array.from(arrayLike); // (*) 경고(arr.pop()); // 월드(메서드 작동)
(*)
줄의 Array.from
객체를 가져와서 반복 가능하거나 배열과 유사한지 검사한 다음 새 배열을 만들고 모든 항목을 여기에 복사합니다.
반복 가능한 경우에도 마찬가지입니다.
// 위의 예에서 범위를 가져온다고 가정합니다. arr = Array.from(range); 경고(arr); // 1,2,3,4,5 (배열을 String으로 변환 가능)
Array.from
의 전체 구문을 사용하면 선택적 "매핑" 기능을 제공할 수도 있습니다.
Array.from(obj[, mapFn, thisArg])
선택적 두 번째 인수 mapFn
각 요소를 배열에 추가하기 전에 적용할 함수일 수 있으며 thisArg
사용하면 this
설정할 수 있습니다.
예를 들어:
// 위의 예에서 범위를 가져온다고 가정합니다. // 각 숫자를 제곱합니다. let arr = Array.from(range, num => num * num); 경고(arr); // 1,4,9,16,25
여기서는 Array.from
사용하여 문자열을 문자 배열로 변환합니다.
str = '??'; // str을 문자 배열로 분할합니다. chars = Array.from(str)을 보자; 경고(문자[0]); // ? 경고(문자[1]); // ? 경고(문자.길이); // 2
str.split
과 달리 문자열의 반복 가능 특성에 의존하므로 for..of
와 마찬가지로 서로게이트 쌍과 올바르게 작동합니다.
기술적으로 여기서는 다음과 동일합니다.
str = '??'; 문자 = []; // Array.from은 내부적으로 동일한 루프를 수행합니다. for (str의 문자를 보자) { chars.push(char); } 경고(문자);
…하지만 더 짧습니다.
여기에 대리 인식 slice
구축할 수도 있습니다.
함수 슬라이스(str, 시작, 끝) { return Array.from(str).slice(start, end).join(''); } str = '???'로 놔두세요; 경고(슬라이스(str, 1, 3) ); // ?? // 기본 메서드는 서로게이트 쌍을 지원하지 않습니다. 경고( str.slice(1, 3) ); // 쓰레기(다른 대리 쌍의 두 조각)
for..of
에서 사용할 수 있는 객체를 iterable 이라고 합니다.
기술적으로 iterable은 Symbol.iterator
라는 메서드를 구현해야 합니다.
obj[Symbol.iterator]()
의 결과를 iterator 라고 합니다. 추가 반복 프로세스를 처리합니다.
반복자에는 {done: Boolean, value: any}
객체를 반환하는 next()
라는 메서드가 있어야 합니다. 여기서 done:true
반복 프로세스의 끝을 나타내며, 그렇지 않으면 value
다음 값입니다.
Symbol.iterator
메소드는 for..of
에 의해 자동으로 호출되지만 직접 호출할 수도 있습니다.
문자열이나 배열과 같은 내장 반복 가능 항목도 Symbol.iterator
구현합니다.
문자열 반복자는 대리 쌍에 대해 알고 있습니다.
색인화된 속성과 length
가진 객체를 유사 배열 이라고 합니다. 이러한 객체에는 다른 속성과 메서드도 있을 수 있지만 배열의 내장 메서드가 부족합니다.
사양을 살펴보면 대부분의 내장 메소드가 "실제" 배열 대신 반복 가능 항목이나 유사 배열과 함께 작동한다고 가정한다는 것을 알 수 있습니다. 왜냐하면 그것이 더 추상적이기 때문입니다.
Array.from(obj[, mapFn, thisArg])
반복 가능하거나 배열과 유사한 obj
에서 실제 Array
만든 다음 배열 메서드를 사용할 수 있습니다. 선택적 인수 mapFn
및 thisArg
사용하면 각 항목에 함수를 적용할 수 있습니다.