반복자와 for...of 루프
1. 반복자
1. 개념
Iterator는 그러한 메커니즘입니다. 다양한 데이터 구조에 대한 통합 액세스 메커니즘을 제공하는 인터페이스입니다. 모든 데이터 구조가 Iterator 인터페이스를 배포하는 한 순회 작업을 완료할 수 있습니다(즉, 데이터 구조의 모든 멤버를 차례로 처리).
Iterator에는 세 가지 기능이 있습니다.
- 하나는 다양한 데이터 구조에 대한 통합되고 편리한 액세스 인터페이스를 제공하는 것입니다.
- 두 번째는 데이터 구조의 구성원을 특정 순서로 정렬할 수 있도록 하는 것입니다.
- 세 번째는 ES6가 for…of 루프를 위한 새로운 순회 명령을 만들었고 Iterator 인터페이스는 주로 for…of 소비에 사용된다는 것입니다.
2. 작동 원리
-
현재 데이터 구조의 시작을 가리키는 포인터 개체를 만듭니다. 즉, traverser 객체는 본질적으로 포인터 객체입니다.
-
next
포인터 개체의 메서드가 처음 호출될 때 포인터는 데이터 구조의 첫 번째 멤버를 가리킬 수 있습니다. -
포인터 개체의 메서드가 두 번째로 호출되면
next
포인터는 데이터 구조의 두 번째 멤버를 가리킵니다. -
next
데이터 구조의 끝을 가리킬 때까지 포인터 개체에서 메서드를 계속 호출합니다 .
메소드가 호출될 때마다 두 개의 속성을 포함하는 객체가 next
반환됩니다 . 그 중 속성은 현재 멤버의 값이고 속성은 순회가 끝났음을 나타내는 부울 값이다.value
done
value
done
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. 기본 Iterator 인터페이스
데이터 구조가 Iterator 인터페이스를 배포하는 한 이 데이터 구조를 "순회 가능"(반복 가능)이라고 합니다.
ES6는 기본 Iterator 인터페이스가 데이터 구조의 속성에 배포된다고 규정합니다 Symbol.iterator
. 즉, 데이터 구조에 Symbol.iterator
속성이 있는 한 "반복 가능한" 것으로 간주할 수 있습니다.
Symbol.iterator
속성 자체는 현재 데이터 구조의 기본 순회자 생성 함수인 함수입니다. 이 함수를 실행하면 반복자가 반환됩니다.
const obj = {
[Symbol.iterator] : function () {
return {
next: function () {
return {
value: 1,
done: true
};
}
};
}
};
ES6는 새로운 순회 명령 for…of
루프를 생성하며 Iterator 인터페이스는 주로 for…of
사용 됩니다.
반복자 인터페이스가 있는 기본 데이터( for...of
순회 가능)
- 정렬
- 지도
- 세트
- 끈
- TypedArray
- 함수의 인수 객체
- NodeList 개체
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 }
4. Iterator 인터페이스 호출 시
경우에 따라 기본적으로 Iterator 인터페이스(즉, 메서드)가 호출되며, Symbol.iterator
아래에서 설명하는 for...of
루프 외에도 여러 가지 경우가 있습니다.
(1) 디스트럭처링 할당
Symbol.iterator 메서드는 배열과 Set 구조를 분해하고 할당할 때 기본적으로 호출됩니다.
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']
(3) 수율*
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 }
(4) 기타 경우
배열의 순회는 순회 인터페이스를 호출하므로 배열을 매개변수로 받아들이는 모든 경우는 실제로 순회 인터페이스를 호출합니다. 다음은 몇 가지 예입니다.
- ...의
- 배열.from()
- Map(), Set(), WeakMap(), WeakSet()(예:
new Map([['a',1],['b',2]])
) - 약속.모두()
- 약속.경주()
2. for...of 루프
1. 어레이
const arr = ['red', 'green', 'blue'];
for(let v of arr) {
console.log(v); // red green blue
}
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
루프는 개체의 키 이름만 가져올 수 있으며 키 값을 직접 가져올 수 없습니다. ES6는 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...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
루프는 배열 arr의 foo 속성을 반환하지 않습니다.
2. 세트 및 맵 구조
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
3. 배열과 같은 객체
배열과 같은 객체에는 여러 클래스가 포함됩니다. 다음은 문자열, DOM NodeList 객체 및 인수 객체에 대한 for...of 루프의 예입니다.
// 字符串
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'
모든 배열과 같은 객체에 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);
}
4. 개체
일반 객체의 경우 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[Symbol.iterator] is not a function
위의 코드는 일반 객체의 경우 for...in
루프가 키 이름을 트래버스할 수 있고 for...of
루프가 오류를 보고함을 나타냅니다.
한 가지 해결책은 메서드를 사용하여 Object.keys
개체의 키 이름 배열을 생성한 다음 배열을 트래버스하는 것입니다.
for (var key of Object.keys(someObject)) {
console.log(key + ': ' + someObject[key]);
}
또 다른 방법은 Generator 함수를 사용하여 개체를 다시 압축하는 것입니다.
const obj = {
a: 1, b: 2, c: 3 }
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
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", "2" 등입니다. for...in
루프는 숫자 키 이름을 반복할 뿐만 아니라 프로토타입 체인의 키를 포함하여 수동으로 추가된 다른 키도 반복합니다.- 어떤 경우에는 for...in 루프가 임의의 순서로 키를 반복합니다.
요컨대, for...in
주기는 주로 다음을 위한 것입니다.트래버스 개체설계상 배열 순회에는 적합하지 않습니다.
for...of
위의 여러 접근 방식과 비교할 때 루핑에는 몇 가지 중요한 이점이 있습니다.
for (let value of myArray) {
console.log(value);
}
- 동일한 간결한 구문을 사용
for...in
하지만for...in
이러한 단점은 없습니다. forEach
메소드와 달리break、continue和return
.- 모든 데이터 구조를 순회하기 위한 통합 작업 인터페이스를 제공합니다.
다음은 문을 사용하여 루프 break
에서 벗어나는 예입니다 .for...of
for (var n of fibonacci) {
if (n > 1000)
break;
console.log(n);
}
위의 예는 피보나치 수열이 1000보다 작거나 같은 항목을 출력합니다. 현재 항목이 1000보다 크면 문을 사용하여 루프를 break
중단합니다 .for...of