[JavaScript] 4-1. JavaScript Iteration

JavaScript Iteration


Iterable

반복 가능한 객체(iterable object)는 for…of 구문과 함께 ES2015 에서 도입되었습니다.
반복 가능한 객체를 다른 객체와 구분짓는 특징은, 객체의 Symbol.iterator 속성에 특별한 형태의 함수가 들어있다는 것입니다.

const str = "hello";
str[Symbol.iterator]; // [Function]

객체의 Symbol.iterator 속성에 특정 형태의 함수가 들어있다면,
이를 반복 가능한 객체(iterable object) 혹은 줄여서 iterable 이라 부르고,
해당 객체는 iterable protocol 을 만족한다고 말합니다.
이런 객체들에 대해서는 ES2015 에서 추가된 다양한 기능들을 사용할 수 있습니다.

내장된 생성자 중 iterable 객체를 만들어내는 생성자에는 아래와 같은 것들이 있습니다.



Iterable 의 사용

어떤 객체가 Iterable 이라면, 그 객체에 대해서 아래의 기능들을 사용할 수 있습니다.

즉, 문자열에 대해서도 위 기능들을 사용할 수 있습니다.

// `for...of`
for (let c of "hello") {
  console.log(c);
}

// spread 연산자
const characters = [..."hello"];

// 분해대입
const [c1, c2] = "hello";

// `Array.from`은 iterable 혹은 array-like 객체를 인수로 받습니다.
Array.from("hello");



Generator 함수

iterable protocol 을 구현하기만 하면 어떤 객체든 iterable 이 될 수 있습니다.
Iterable 을 구현하는 가장 쉬운 방법은 ES2015 에 도입된 generator 함수를 사용하는 것입니다.
Generator 함수는 iterable 객체를 반환하는 특별한 형태의 함수입니다.

// generator 함수 선언하기
function* gen1() {
  // ...
}

// 표현식으로 사용하기
const gen2 = function*() {
  // ...
};

// 메소드 문법으로 사용하기
const obj = {
  *gen3() {
    // ...
  }
};

Generator 함수를 호출하면 객체가 생성되는데, 이 객체는 iterable protocol 을 만족합니다.
즉, Symbol.iterator 속성을 갖고 있습니다.

function* gen1() {
  // ...
}

// `gen1`를 호출하면 iterable이 반환됩니다.
const iterable = gen1();

iterable[Symbol.iterator]; // [Function]

Generator 함수 안에서는 yield 라는 특별한 키워드를 사용할 수 있습니다.
Generator 함수 안에서 yield 키워드는 return 과 유사한 역할을 하며,
iterable 의 기능을 사용할 때 yield 키워드 뒤에 있는 값들을 순서대로 넘겨줍니다.

function* numberGen() {
  // function뒤에 *을 붙여주면 generator 함수로 선언을 할 수 있습니다.
  yield 1; // yield는 return과 같으나 return은 실행과 동시에 함수의 동작을 종료하지만 yield는 그렇지 않습니다.
  yield 2;
  yield 3;
}

// 1, 2, 3이 순서대로 출력됩니다.
for (let n of numberGen()) {
  console.log(n);
}

yield\* 표현식을 사용하면, 다른 generator 함수에서 넘겨준 값을 대신 넘겨줄 수도 있습니다.

function* numberGen() {
  yield 1;
  yield 2;
  yield 3;
}

function* numberGen2() {
  yield* numberGen();
  yield* numberGen();
}

// 1, 2, 3, 1, 2, 3이 순서대로 출력됩니다.
for (let n of numberGen2()) {
  console.log(n);
}

yield 키워드를 제외하면, generator 함수 내부의 동작 방식은 일반적인 함수와 별반 다르지 않습니다.
즉, 다른 함수에서 할 수 있는 일이라면 generator 함수 안에서도 모두 할 수 있습니다.

// 등차수열 생성하기
function* range(start = 0, end = Infinity, step = 1) {
  for (let i = start; i < end; i += step) {
    yield i;
  }
}

// 피보나치 수열 생성하기
function* fibonacci(count = Infinity) {
  let x = 1;
  let y = 1;
  for (let i = 0; i < count; i++) {
    yield x;
    [x, y] = [y, x + y];
  }
}

// 하나의 항목을 계속 넘겨주기
function* repeat(item, count = Infinity) {
  for (let i = 0; i < count; i++) {
    yield item;
  }
}

// 여러 요소를 반복해서 넘겨주기
function* repeatMany(array) {
  while (true) {
    for (let item of array) {
      yield item;
    }
  }
}

Generator 함수를 사용할 때 주의할 점이 있습니다.

// Generator 함수로부터 생성된 iterable은 한 번만 사용될 수 있습니다.
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

const iter = gen();

for (let n of iter) {
  // 잘 출력됩니다.
  console.log(n);
}
for (let n of iter) {
  // `iter`는 한 번 사용되었으므로, 이 코드는 실행되지 않습니다.
  console.log(n);
}
// Generator 함수 내부에서 정의된 일반 함수에서는 `yield` 키워드를 사용할 수 없습니다.
function* gen2() {
  // 아예 문법 오류가 납니다. (Unexpected token)
  function fakeGen() {
    yield 1;
    yield 2;
    yield 3;
  }
  fakeGen();
}



Iterable protocol 은 다음 주에 쉬는 동안 공부할 예정입니다.



2. Today I Found Out

다소 생소한 내용이여서 조금은 어렵게 느껴지기도 했지만
기존에 배웠던 기능과 완전히 다르지 않고 많이 유사해서
공부하면서 많이 익숙해졌습니다.
다양한 사용사례를 안봐서 어떻게 써야할지 아직 감이 오지 않지만
다양한 오픈소스를 볼 때 알고 보는 것과 모르고 보는 것의 차이가 크기에
공부를 하고 오픈소스를 볼 때 나오면 참 반갑겠다라는 생각을 하였고
리액트에 들어가면 종종 마주치게 된다고 하니
수업떄는 간략히 필요한 정도만 배웠지만
시간을 할애해서 조금 더 알아두고 갈 계획입니다.



https://helloworldjavascript.net/pages/260-iteration.html

https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Iterators_and_Generators