YDKJSY 연습 문제 [1권]

시소·2024년 7월 15일

You Don't Know JS Yet

목록 보기
1/1

You Don't Know JavaScript Yet의 부록에 있는 연습 문제를 풀어본 내용을 정리하였습니다.
함께 소개된 모범 답안은 굳이 옮겨 놓지 않았으나 상세한 내용은 원문 Repository에 있는 "Suggested Solutions"에서 확인할 수 있습니다.

1. 비교 연습하기

주어진 조건

① 함수 scheduleMeeting()은 "hh:mm"형태(24시간을 기준으로 시간을 나타내는 문자열)로 된 2가지 인수를 받는다.

  • startTime: 회의 시작 시간
  • durationMinutes: 분 단위의 회의 지속 시간

② 변수 dayStartdayEnd에는 근무 종료 시간이 할당된다.

③ 함수는 회의가 근무 시간 내에 이뤄질 경우 true를, 그렇지 않으면 false를 반환한다.

const dayStart = "07:30";
const dayEnd = "17:45";

function scheduleMeeting(startTime,durationMinutes) {
    // ..TODO..
}

scheduleMeeting("7:00",15);     // false
scheduleMeeting("07:15",30);    // false
scheduleMeeting("7:30",30);     // true
scheduleMeeting("11:30",60);    // true
scheduleMeeting("17:00",45);    // true
scheduleMeeting("17:30",30);    // false
scheduleMeeting("18:00",15);    // false

작성한 코드

const dayStart = "07:30";
const dayEnd = "17:45";

const now = Date.now();
const dayStartTime = timeStrToDate(dayStart);
const dayEndTime = timeStrToDate(dayEnd);

function timeStrToDate(timeStr) {
  if (typeof timeStr !== "string") return timeStr;
  
  const [hours, minutes] = timeStr.split(':').map(Number);
  
  return new Date(now).setHours(hours, minutes, 0, 0);
}

function scheduleMeeting(startTime, durationMinutes) {
  const sTime = timeStrToDate(startTime);
  const eTime = sTime + (1000 * 60 * durationMinutes);
  
  return sTime >= dayStartTime && eTime <= dayEndTime;
}
  • timeStrToDate() 함수를 별도로 정의하여, 시간을 나타내는 문자열을 Date 객체로 변환하도록 했다.
  • 날짜에 대한 기준이 주어지지 않았기에 현재 날짜(Date.now())를 기준으로 setHours() 메서드를 사용해 시간만 다르게 설정해 주었다.
  • 회의 시작 시간(sTime) 및 종료 시간(eTime)은 모두 Date() 생성자를 통해 만들어진 객체로써 밀리초 단위의 UNIX 타임스탬프 값을 가지고 있다.
  • 최종적으로 이 타임스탬프 값을 가지고 비교 연산자로 비교하여 주어진 조건을 만족하는지 검사한다.

2. 클로저 연습하기

주어진 조건

① 함수 range()는 2개의 숫자로 된 인자를 받으며, 각 숫자는 원하는 범위의 시작과 끝을 나타낸다.

② 2번째 인자가 없는 경우에는 2번째 인자를 넘길 수 있도록 하는 함수가 반환되어야 한다.

function range(start, end) {
  // ..TODO..
}

range(3, 3); // [3]
range(3, 8); // [3, 4, 5, 6, 7, 8]
range(3, 0); // []

var start3 = range(3);
var starrt4 = range(4);

start3(3); // [3]
start3(8); // [3, 4, 5, 6, 7, 8]
start3(0); // []

start4(6); // [4, 5, 6]

작성한 코드

function range(start, end) {
  if (end !== undefined) {
    return generateRange(start, end);
  }
  
  return function(last) {
    return generateRange(start, last);
  } 
}

function generateRange(start, end) {
  return Array.from({ length: end - start + 1 }, (_, i) => start + i);
}
  • range()는 인자에 따른 처리와 클로저 생성을 담당한다.
    • end가 전달된 경우, 즉시 범위를 생성해 반환한다.
    • end가 전달되지 않은 경우, 클로저를 반환한다.
      • 해당 클로저는 시작 값을 start로 미리 설정해 뒀다가
      • 나중에 끝 값을 지정하여 범위를 생성하도록 만든다.
      • start는 내부 상태로써 계속 유지되며,
      • 다양한 끝 값을 받아 호출하는 시점에 해당 끝 값을 기반으로 범위를 생성한다.
  • 공통된 로직은 generateRange()를 별도로 정의하여 재사용 하도록 만들었다. (실제 범위 생성 담당)

3. 프로토타입 연습하기

주어진 조건

① 독립적으로 돌아가는 릴 3개가 있는 슬롯머신이 있다.

  • spin() 실행: 3개의 릴이 돌아간다.
  • display() 실행: 결과가 출력된다.

② 릴 하나의 동작은 reel 객체에 정의되어 있다. 슬롯머신에는 여러 개의 릴이 있으므로 각 릴은 해당 객체를 상속받아야 한다. 추가로 릴에는 position 프로퍼티도 필요하다.

③ 일반적으로 슬롯머신의 릴은 결과에 해당하는 심볼 하나(position)와 앞 심볼(position-1), 뒷 심볼(position+1)를 함께 표시한다. 따라서 display()를 실행하면 3x3 그리드에 총 9개의 기호가 출력되어야 한다.

🪄 힌트 )

  • 나머지 연산자(%)를 사용하면 릴을 원형으로 만들 수 있다.
  • Object.create()로 프로토타입이 있는 객체를 만들어라. 프로토타입 연결이 만들어지면 위임 덕분에 상속 객체들은 메서드 호출 시 this 컨텍스트를 공유하게 된다.
  • 세 릴의 position을 보여주기 위해 객체 reel을 직접 수정하는 대신, 다시 Object.create()를 사용해서 임시 객체를 하나 만들고, 여기에서 위임을 통해 position을 관리하라.
function randMax(max) {
  return Math.trunc(1E9 * Math.random()) % max;
}

var reel = {
  symbols: [
    "♠", "♥", "♦", "♣", "☺", "★", "☾", "☀"
  ],
  spin() {
    if (this.position == null) {
      this.position = randMax(
        this.symbols.length - 1
      );
    }
    this.position = (
      this.position + 100 + randMax(100)
    ) % this.symbols.length;
  },
  display() {
    if (this.position == null) {
      this.position = randMax(
        this.symbols.length - 1
      );
    }
    return this.symbols[this.position];
  }
};

var slotMachine = {
  reels: [
    // ..TODO..
    // 슬롯머신에는 3개의 릴이 필요하다
    // 힌트: Object.create(..)
  ],
  spin() {
    this.reels.forEach(function spinReel(reel){
      reel.spin();
    });
  },
  display() {
    // ..TODO..
  }
};

slotMachine.spin();
slotMachine.display();
// ☾ | ☀ | ★
// ☀ | ♠ | ☾
// ♠ | ♥ | ☀

slotMachine.spin();
slotMachine.display();
// ♦ | ♠ | ♣
// ♣ | ♥ | ☺
// ☺ | ♦ | ★

작성한 코드

var slotMachine = {
  reels: [
    Object.create(reel),
    Object.create(reel),
    Object.create(reel)
  ],
  spin() {
    this.reels.forEach(function spinReel(reel){
      reel.spin();
    });
  },
  display() {
    const lines = []; 
      
    for (let i = -1; i <= 1; i++) {
      const line = this.reels.map(function generateLine(reel) {
        const slot = Object.create(reel);
        
        slot.position = (
          reel.symbols.length + reel.position + i
        ) % reel.symbols.length;
        
        return slot.display();
      });
      
      lines.push(line.join(' | '));
    }
    
    return lines.join('\n');
  }
};
  • slotMachine.reels는 3개의 릴을 가진 슬롯 머신을 의미한다.
  • slotMachine.display()는 3개의 릴을 가진 슬롯 머신의 현재 상태를 표시한다.
    • slot이 현재 나타내는 심볼과 그 앞/뒤 심볼을 함께 나타내기 위해 -1...1 loop에서 작업을 수행한다.
    • 각 slot이 reel 객체의 프로토타입을 상속받도록 해 display()를 호출할 수 있다.
    • 개별 slot의 position은 순환 구조(마지막 심볼 다음에 첫 번째 심볼을 가르키도록)를 따르므로, 나머지 연산을 활용하였다.
      • 만약 current symbol이 7번째 인 경우, next symbol은 (8+7+1)%8 = 0번째
      • 만약 current symbol이 0번째 인 경우, prev symbol은 (8+0-1)%8 = 7번째
    • 만약 3개의 릴의 postion이 순서대로 4, 3, 7 이라고 가정하면
    • lines[0] 에는 순서대로 3, 2, 6 번째 심볼을 표시해 현재 릴의 이전 위치를 나타내며
    • lines[1] 에는 순서대로 4, 3, 7 번째 심볼을 표시해 현재 릴의 위치를 나타내고
    • lines[2] 에는 순서대로 5, 4, 0 번째 심볼을 표시해 현재 릴의 다음 위치를 나타낸다.
profile
배우고 익힌 것을 나만의 언어로 정리하는 공간 ..🛝

0개의 댓글