코드스피츠 스터디 2회차

0
post-thumbnail
post-custom-banner

Interface


  1. 인터페이스란 사양에 맞는 값과 연결된 속성 키의 셋트
  2. 어떤 Object라도 인터페이스의 정의를 충족시킬 수 있다.
  3. 하나의 Object는 여러 개의 인터페이스를 충족시킬 수 있다.

꽤나 어려운 말로 ECMAScript에서 자바스크립트의 인터페이스를 설명한다.

조금 쉽게 설명하자면 어떠한 키를 가지고 특정 값을 특정 개수로 받아 어떠한 결과를 반환하는 것이다.

// 예시
{
  test(str){return true;}
}
// test란 키를 가지고 1개의 문자열을 인자로 받으며 boolean 값을 리턴하는 함수. 

Iterator Interface


  1. next라는 키를 갖고
  2. 값으로 인자를 받지 않고 IteratorResultObject를 반환하는 함수가 온다.
  3. IteratorResultObjectvaluedone이라는 키를 갖고 있다.
  4. 이 중 done은 계속 반복할 수 있을지 없을지에 따라 불린 값을 반환한다.
{
  next(){
    return {value: 1, done: false};
  }
}
{
  data: [1,2,3,4],
  next(){
    return {
      done: this.data.length == 0;
      value: this.data.pop()
    };
  }
}

이러한 형태를 반환하고 있는 것을 이터레이터 객체라고 한다.

Iterable Interface


  1. Symbol.iterator라는 키를 갖고
  2. 값으로 인자를 받지 않고 Iterator Object를 반환하는 함수가 온다.
{
 [Symbol.iterator](){
  return {
    next(){
      return {value: 1, done: false};
    }
  }
}

이렇게 심볼 이터레이터 키를 가지고 인터레이터 객체를 반환하는 함수를 이터러블라고 한다.

Loop to iterator


While문으로 살펴보는 Iterator

let data = [1,2,3,4];
while(data.length > 0){ //계속 반복할지 판단
 data.pop();
}
{
  data: [1,2,3,4],
  next(){
    return {
      // 계속 반복할지 판단하는 구간
      done: this.data.length == 0;
      // 반복 시 처리 해야 할 코드
      value: this.data.pop()
    };
  }
}

반복자체를 하지는 않지만 외부에서 반복을 하려고 할 때 반복에 필요한 조건과 실행을 미리 준비해 둔 객체
이러한 것을 self-description이라고 한다.

이렇게 미리 반복에 대한 준비를 해두고 필요할 때 필요한 만큼 반복을 하고 반복을 재현할 수 있는 것이 이터레이터이다.

Iterator를 사용하는 이유

그렇다면 우리는 왜 반복문을 사용하지 않고 iterator를 사용 해야 하는 것인가 ?

👨‍🏫: 여러분이 구구단과 같은 쉬운 이중 for문이 아닌 피벗 테이블에 대한 합계열을 이중 for문으로 짠다고 했을 때 정확하게 가능할까 ?

iterator는 스스로가 알고리즘을 가지고 있기 때문에 몇번을 반복해도 안정적이게 반복될 수 있다.

iterator를 사용함으로서 우리는 반복기와 반복 해야하는 조건을 분리함으로서 안정성을 얻을 수 있다.

사용자 반복 처리기

const iter ={
  arr: [1,2,3,4],
  [Symbol.iterator](){return this;},
  next(){
   return {
     done: this.arr.length == 0,
     value: this.arr.pop()
  };
 }
}

const loop = (iter, f) => {
 // iterable이라면 iterator를 얻음
 if(typeof iter[Symbol.iterator] == 'function'){
  iter = iter[Symbol.iterator]();
 }
 else return;
 // iteratorObject가 아니라면 건너뜀
 if(typeof iter.next != 'function') return;
  
 do{
  const v = iter.next();
  if(v.done) return; // 종료처리
  f(v.value); // 현재 값을 전달
 }
 while(true);
}

loop(iter, console.log);
// 4
// 3
// 2
// 1

내장 반복 처리기

const iter ={
  arr: [1,2,3,4],
  [Symbol.iterator](){return this;},
  next(){
   return {
     done: this.arr.length == 0,
     value: this.arr.pop()
  };
 }
}

// Array destructuring 배열 해체
const [a, ...b] = iter;
console.log(a, b); // 4, [3,2,1]

// Spread 펼치기
const c = [...iter];
console.log(c); // [4,3,2,1]

// Rest parameter 나머지 인자
const test = (...arg) => console.log(arg)
test(...iter);
// [4,3,2,1]

For of

const iter ={
  [Symbol.iterator](){return this;},
  arr: [1,2,3,4],
  next(){
   return {
     done: this.arr.length == 0,
     value: this.arr.pop()
  };
 }
}

for(const v of iter){
 console.log(v);
}
// 4
// 3
// 2
// 1

Generator


제너레이터는 function 뒤에 *을 붙여 사용 할 수 있다.

일반 함수는 하나의 값만을 반환하지만 제너레이터는 여러 개의 값을 필요에 따라 하나씩 반환 할 수 있도록 해준다.

Iterator를 간략하게 만들어 놓은 함수라고 생각하면 보기 편하다.

제너레이터 함수는 yield라는 문을 만나게 되는데 yield을 통해 제어를 할 수 있게 된다.

const generator = function*(max){
 let cursor = 0;
 while(cursor < max){
  yield cursor * cursor;
  cursor++;
 }
}

console.log([...generator(5)]);
// [0, 1, 4, 9, 16]

for(const v of generator(5)){
  console.log(v);
}
// 0
// 1
// 4
// 9
// 16

위의 코드를 보게 되면 while문은 Iterator의 next()와 같은 성격을 띄게 되고, done과 value는 yield가 처리하게 된다.

yield의 done은 모두 false이며, 더이상 yield하지 않고 제너레이터를 빠져 나올 때 true로 바뀌게 된다.

또한 제너레이터는 반환되는 값은 iterator이자 iterable 객체를 반환한다.

느낀점


조금은 생소한 이터러블, 이터레이터, 제너레이터에 대해 학습을 했다.

스프레드 문법이 이터러블 객체를 가지고 있는 이터레이터에만 사용이 가능하고 for of문 또한 이터러블 객체를 가지고 있어야 사용이 가능하다는 것을 오늘에서야 배우게 되었다.

이전에 생각없이 ES6+ 문법을 사용해왔다면 이젠 동작에 대한 이해와 함께 사용하게 되었다.

다만 아쉬운 점은 아직까지 Rest parameter와 Spread 문법 그리고 for of문에 대한 학습 외에 공감이 가지 않은 것이 문제이다.

제너레이터는 Redux-saga에서 비동기를 처리 할 때 사용 하는데 이 또한 경험을 하지 못해서 머리로는 이해하지만 와닿지 않은 것이 조금 아쉽다.

이러한 기본적인 문법의 이해를 바탕으로 응용하고 사용을 해봐야 조금 더 머리에 들어올 것 같다.


post-custom-banner

0개의 댓글