자바스크립트에서 제너레이터와 이터레이터를 사용하는 방법
기존 루프를 사용하여 데이터 수집을 반복하면 특히 대량의 데이터를 처리할 때 번거롭고 느려질 수 있습니다.
JavaScript Generators 및 Iterator는 대규모 데이터 컬렉션을 효율적으로 반복하기 위한 솔루션을 제공합니다. 이를 사용하여 반복 흐름을 제어하고, 한 번에 하나씩 값을 생성하고, 반복 프로세스를 일시 중지하고 다시 시작할 수 있습니다.
여기서는 JavaScript 반복자의 기본 사항과 내부를 다루고 생성기를 사용하여 수동으로 반복자를 생성하는 방법을 다룹니다.
JavaScript 반복자
반복자는 반복자 프로토콜을 구현하는 JavaScript 객체입니다. 이러한 개체는 다음 메서드 를 사용하여 이를 수행합니다 . 이 메서드는 IteratorResult 인터페이스를 구현하는 개체를 반환합니다 .
IteratorResult 인터페이스는 done 및 value 의 두 가지 속성으로 구성 됩니다 . done 속성 은 반복자가 시퀀스의 다음 값을 생성할 수 있는 경우 false를 반환하고 반복자가 시퀀스를 완료한 경우 true를 반환 하는 부울입니다 .
value 속성은 시퀀스 중에 반복자가 반환하는 JavaScript 값입니다 . 반복자가 시퀀스를 완료하면( 완료되면 === true ) 이 속성은 undefined 를 반환합니다 .
이름에서 알 수 있듯이 이터레이터를 사용하면 배열이나 맵과 같은 JavaScript 개체를 “반복”할 수 있습니다. 이 동작은 반복 가능한 프로토콜로 인해 가능합니다.
JavaScript에서 iterable 프로토콜은 for…of 루프 와 같이 반복할 수 있는 객체를 정의하는 표준 방법입니다 .
예를 들어:
const fruits = ["Banana", "Mango", "Apple", "Grapes"];
for (const iterator of fruits) {
console.log(iterator);
}
/*
Banana
Mango
Apple
Grapes
*/
이 예제는 for…of 루프를 사용하여 fruits 배열을 반복합니다 . 각 반복에서 현재 값을 콘솔에 기록합니다. 이것은 배열이 반복 가능하기 때문에 가능합니다.
Arrays, Strings, Sets 및 Maps와 같은 일부 JavaScript 유형은(또는 프로토타입 체인의 객체 중 하나) @@iterator 메서드를 구현하기 때문에 내장 반복 가능합니다 .
객체와 같은 다른 유형은 기본적으로 반복할 수 없습니다.
예를 들어:
const iterObject = {
cars: ["Tesla", "BMW", "Toyota"],
animals: ["Cat", "Dog", "Hamster"],
food: ["Burgers", "Pizza", "Pasta"],
};
for (const iterator of iterObject) {
console.log(iterator);
}
// TypeError: iterObject is not iterable
이 예제는 반복할 수 없는 객체를 반복하려고 할 때 어떤 일이 발생하는지 보여줍니다.
객체를 반복 가능하게 만들기
객체를 반복 가능하게 만들려면 객체에 Symbol.iterator 메서드 를 구현해야 합니다 . 반복 가능하려면 이 메서드는 IteratorResult 인터페이스를 구현하는 객체를 반환해야 합니다.
아래 코드 블록은 iterObject 를 사용하여 객체를 반복 가능하게 만드는 방법의 예를 제공합니다 .
먼저, 함수 선언을 사용하여 iterObject 에 Symbol.iterator 메서드를 추가합니다.
이렇게:
iterObject[Symbol.iterator] = function () {
// Subsequent code blocks go here...
}
다음으로 반복 가능하게 만들려는 개체의 모든 키에 액세스해야 합니다. 개체의 열거 가능한 속성 배열을 반환하는 Object.keys 메서드 를 사용하여 키에 액세스할 수 있습니다 . iterObject 의 키 배열을 반환하려면 this 키워드를 Object.keys 의 인수로 전달하십시오 .
예를 들어:
let objProperties = Object.keys(this)
이 배열에 액세스하면 개체의 반복 동작을 정의할 수 있습니다.
다음으로 개체의 반복을 추적해야 합니다. 카운터 변수를 사용하여 이를 달성할 수 있습니다.
예를 들어:
let propertyIndex = 0;
let childIndex = 0;
첫 번째 카운터 변수를 사용하여 개체 속성을 추적하고 두 번째 카운터 변수를 사용하여 속성의 자식을 추적합니다.
다음으로 next 메서드를 구현하고 반환해야 합니다 .
이렇게:
return {
next() {
// Subsequent code blocks go here...
}
}
next 메서드 내에서 전체 개체가 반복될 때 발생하는 엣지 케이스를 처리해야 합니다. 극단적인 경우를 처리하려면 값이 undefined 로 설정되고 done 이 true 로 설정된 객체를 반환해야 합니다 .
엣지 케이스를 처리하는 방법은 다음과 같습니다.
if (propertyIndex > objProperties.length - 1) {
return {
value: undefined,
done: true,
};
}
다음으로, 앞에서 선언한 카운터 변수를 사용하여 개체 속성과 해당 자식 요소에 액세스해야 합니다.
이렇게:
// Accessing parent and child properties
const properties = this[objProperties[propertyIndex]];
const property = properties[childIndex];
다음으로 카운터 변수를 증가시키기 위한 논리를 구현해야 합니다. 논리는 속성의 배열에 더 이상 요소가 없을 때 childIndex를 재설정하고 개체의 다음 속성으로 이동해야 합니다. 또한 현재 속성의 배열에 여전히 요소가 있는 경우 childIndex 를 증가시켜야 합니다 .
예를 들어:
// Index incrementing logic
if (childIndex >= properties.length - 1) {
// if there are no more elements in the child array
// reset child index
childIndex = 0;
// Move to the next property
propertyIndex++;
} else {
// Move to the next element in the child array
childIndex++
}
마지막으로 done 속성이 false 로 설정 되고 value 속성이 반복의 현재 자식 요소로 설정된 객체를 반환합니다 .
예를 들어:
return {
done: false,
value: property,
};
완료된 Symbol.iterator 함수는 아래 코드 블록과 유사해야 합니다.
iterObject[Symbol.iterator] = function () {
const objProperties = Object.keys(this);
let propertyIndex = 0;
let childIndex = 0;
return {
next: () => {
//Handling edge case
if (propertyIndex > objProperties.length - 1) {
return {
value: undefined,
done: true,
};
}
// Accessing parent and child properties
const properties = this[objProperties[propertyIndex]];
const property = properties[childIndex];
// Index incrementing logic
if (childIndex >= properties.length - 1) {
// if there are no more elements in the child array
// reset child index
childIndex = 0;
// Move to the next property
propertyIndex++;
} else {
// Move to the next element in the child array
childIndex++
}
return {
done: false,
value: property,
};
},
};
};
이 구현 후 iterObject 에서 for…of 루프를 실행하면 Symbol.iterator 메서드 를 구현하므로 오류가 발생하지 않습니다 .
위에서 했던 것처럼 반복자를 수동으로 구현하는 것은 오류가 발생하기 쉽고 논리를 관리하기 어려울 수 있으므로 권장되지 않습니다.
자바스크립트 생성기
JavaScript 생성기는 언제든지 실행을 일시 중지하고 다시 시작할 수 있는 기능입니다. 이 동작을 통해 시간이 지남에 따라 일련의 값을 생성할 수 있습니다.
제너레이터를 반환하는 함수인 제너레이터 함수는 이터레이터 생성에 대한 대안을 제공합니다.
JavaScript에서 함수 선언을 만드는 것과 같은 방식으로 생성기 함수를 만들 수 있습니다. 유일한 차이점은 별표( * )를 function 키워드에 추가해야 한다는 것입니다.
예를 들어:
function* example () {
return "Generator"
}
JavaScript에서 일반 함수를 호출하면 return 키워드 로 지정된 값을 반환하거나 그렇지 않으면 정의되지 않습니다 . 그러나 생성기 함수는 어떤 값도 즉시 반환하지 않습니다. 변수에 할당할 수 있는 Generator 개체를 반환합니다.
반복자의 현재 값에 액세스하려면 Generator 개체에서 next 메서드를 호출합니다.
예를 들어:
const gen = example();
console.log(gen.next()); // { value: 'Generator', done: true }
위의 예에서 value 속성은 반환 키워드 에서 왔으며 효과적으로 생성기를 종료합니다. 이 동작은 일반적으로 생성기 함수에서 바람직하지 않은데, 일반 함수와 구별되는 것은 실행을 일시 중지하고 다시 시작하는 기능이기 때문입니다.
수익률 키워드
yield 키워드는 제너레이터 함수의 실행을 일시 중지하고 뒤에 오는 값을 반환하여 제너레이터의 값을 반복하는 방법을 제공합니다 .
예를 들어:
function* example() {
yield "Model S"
yield "Model X"
yield "Cyber Truck"
return "Tesla"
}
const gen = example();
console.log(gen.next()); // { value: 'Model S', done: false }
위의 예에서 next 메서드가 예제 생성기 에서 호출되면 yield 키워드를 만날 때마다 일시 중지됩니다 . done 속성도 반환 키워드를 만날 때까지 false 로 설정됩니다 .
이를 시연하기 위해 예제 생성기에서 next 메서드를 여러 번 호출하면 다음과 같은 결과가 출력됩니다.
console.log(gen.next()); // { value: 'Model X', done: false }
console.log(gen.next()); // { value: 'Cyber Truck', done: false }
console.log(gen.next()); // { value: 'Tesla', done: true }
console.log(gen.next()); // { value: undefined, done: true }
for…of 루프를 사용하여 Generator 객체를 반복할 수도 있습니다 .
예를 들어:
for (const iterator of gen) {
console.log(iterator);
}
/*
Model S
Model X
Cyber Truck
*/
반복자와 생성기 사용
반복자와 생성기는 추상적인 개념처럼 보일 수 있지만 그렇지 않습니다. 무한 데이터 스트림 및 데이터 수집 작업을 할 때 유용할 수 있습니다. 이를 사용하여 고유한 식별자를 만들 수도 있습니다. MST(MobX-State-Tree)와 같은 상태 관리 라이브러리도 후드에서 사용합니다.
답글 남기기