for, for in, for of, forEach

Python에는 for와 while이라는 반복문이 있고, 이 둘은 범위냐 조건이냐의 차이만 빼면 반복문이라는 기본 골자는 같은데, 이런 게 자바스크립트에도 당연히 있다. 라디오버튼 뺑뺑이 돌면서 얘가 뭘 체크했나 보는 것도 반복문의 일이기 때문.

그런데 이 반복문… Python처럼 심플하지 않아요…


for, for in, for of

See the Pen for, for in, for of by koreanraichu (@koreanraichu) on CodePen.

얘네들은 Pyhlogenic tree로 치자면 그 트리가 막 갈라지는 와중에도 진화적으로 매우 유사한 homolog이기 때문에 또이또이 쌤쌤임을 인정받은 애들… 맞는 비유인지는 모르겠지만, 자매나 형제같은 느낌이다. 그래서 형식 말고 크게 다를 건 없다.

let pokemon = ['김부추씨','김상추씨','김배추씨','김양상추씨','김양배추씨','김후추씨']

for (let i = 0;i < pokemon.length;i++) {
  let p_tag = document.createElement('p')
  p_tag.innerText = pokemon[i]
  pokemon_div.appendChild(p_tag)
}
//for

for

let num_list = [806, 26, 607, 601, 752, 504, 485, 162, 885, 361, 309, 480, 401, 313, 771, 141, 9, 526, 390, 237]

for (let i in num_list) {
  let p_tag = document.createElement('p')
  if (num_list[i] % 4 == 0) {
    p_tag.innerText = num_list[i]
    for_in_div.appendChild(p_tag)
  }
}
// for in

for in

for (let i of num_list) {
  let p_tag = document.createElement('p')
  if (i % 3 == 0) {
    p_tag.innerText = i
    for_of_div.appendChild(p_tag)
  }
}
//for of

for of

각 소스에서 이 반복문이 하는 일은

  1. 포켓몬 리스트의 요소를 내용으로 하는 p태그를 생성하라
  2. 위에 있는 숫자 리스트에서 4의 배수를 p태그로 생성하라
  3. 위에 있는 숫자 리스트에서 3의 배수를 p태그로 생성하라

이렇게 세 개다. 그리고 세 반복문은 주어진 일을 하기 위해 각각 주어진 배열에 접근해 해당 요소를 가져와 p태그로 만들어서 내보내게 된다. for of는 파이썬으로 치자면 for i in (리스트)로 직접 접근하는 것으로, 저 경우 i에 들어가는 게 배열이 담고 있는 요소가 된다. DOM이면 DOM, 숫자면 숫자, 문자면 문자 이런 식. 반면 위의 두 반복문은 i 자체는 숫자라 인덱싱 절차가 따로 필요하다. 즉 for in이 for i in (리스트)면 나머지 둘은 for i in range(n)으로 인덱스 번호 만들어서 인덱싱 하는 것.

forEach

얘는 위에 셋과는 성격이 조금 다르다. Phylogenic tree로 치자면 쭉 내려가다가 마지막에 가지가 한번 갈라지고 그 다음에 for, for in, for of가 갈라지는 정도. 왜냐하면 얘는 배열에서 뭘 가져온다는 개념이 아니라 배열을 돌면서 주어진 함수를 실행하는 반복문이기 때문이다. 물론 for로 구현할 수 있는 건 forEach로도 구현할 수 있다.

See the Pen For and Foreach by koreanraichu (@koreanraichu) on CodePen.

여기서 for와 forEach의 차이점을 볼 수 있다.

let p_for = document.querySelectorAll('.for > p')
let p_forEach = document.querySelectorAll('.foreach > p')
// querySlectorAll은 배열로 가져온다

let number_list = ['일','이','삼']

for (let i = 0;i < p_for.length;i++) {
  p_for[i].innerText = number_list[i]
}
//for

p_forEach.forEach(function(element, index){
  element.innerText = number_list[index]
})
//forEach

아니 저거 저렇게 선택되는 줄 몰랐음… 아무튼… 위 코드가 하는 일은 div 안에 있는 p태그의 내용을 number_list에 있는 일, 이, 삼으로 바꾸는 것이다. (원문은 외국어) 둘 다 querySelectorAll()로 가져오기 때문에 가져왔을 때 상태는 배열이고, 배열 안에 p태그 세 개가 담겨 있기 때문에 for나 forEach를 사용해서 안에 있는 p태그의 내용을 바꿔줘야 한다.

for문은 말 그대로 위의 배열에서 i번째 요소를 num_list의 i번째 요소로 바꾸는 대단히 심플한 코드인데… forEach에 저거 뭡니까… 아… 저거는 요소와 인덱스로 함수 돌린다는 얘기인데, querySelectAll()로 가져온 배열을 순회하면서 배열이 담고 있는 요소(p태그)를 num_list[인덱스]의 요소로 채운다는 얘기다. 저 인덱스는 querySelectorAll()로 가져온 배열의 인덱스를 말한다. 아니 실화임.

See the Pen For and Foreach 2 by koreanraichu (@koreanraichu) on CodePen.

여기서는 for of와 forEach를 비교해보자.

let sqrt_n = document.querySelector(".sqrt")
let square_n = document.querySelector(".square")
let num_list = [1,2,3,4,5,6,7,8,9,10]
//sqrt: 제곱근(루트)
//square: 2제곱(3제곱은 cube)

for (let i of num_list) {
  let p_tag = document.createElement('p')
  p_tag.innerText = `sqrt ${i}: ${(i ** 0.5).toFixed(3)}`
  sqrt_n.appendChild(p_tag)
}
// 리스트에 있는 수의 2제곱근을 출력(소수점 이하 3자리에서 반올림)

num_list.forEach(function(element){
  let p_tag = document.createElement('p')
  p_tag.innerText = element ** 2
  square_n.appendChild(p_tag)
})
//리스트에 있는 수의 제곱을 출력

위 코드는 배열 안에 있는 수의 제곱근을 소수점 세자리까지 출력하는 코드이고, 아래 코드는 배열 안에 있는 수를 제곱해서 출력하는 코드이다. 아, 왜 **냐면 자바스크립트와 파이썬에서 ^는 XOR을 의미한다. 그래서 파이썬이나 자바스크립트로 2 ^ 2 하면 4가 아니라 0이 나온다. (XOR은 입력 두 개가 달라야 참임)

두 코드가 크게 다를 건 없다. 위 코드는 num_list에 있는 요소(숫자)를 가져와 0.5제곱(2루트)을 하고 innerText로 만든 다음 그걸 추가하는거고, 아래는 num_list를 순회하면서 안에 있는 요소(숫자)의 2승을 구하고 그걸 innerText로 만들어서 추가한다.

See the Pen For and Foreach 3 by koreanraichu (@koreanraichu) on CodePen.

for in과 forEach를 비교해보자.

let num_list = [1,2,3,4,5,6,7,8,9,0,11,12,13,14,15,16,17,18,19,20]
let num_list2 = [21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40]
//아니 자바스크립트는 list(range()) 없나?
let odd_number = document.querySelector('.odd')
let even_number = document.querySelector('.even')

for (let i in num_list) {
  let p_tag = document.createElement('p')
  if (num_list[i] % 2 == 0) {
    p_tag.innerText = `${num_list[i]} is even number`
    even_number.appendChild(p_tag)
  }
  else {
    p_tag.innerText = `${num_list[i]} is odd number`
    odd_number.appendChild(p_tag)
  }
}

num_list2.forEach(function(element){
  let p_tag = document.createElement('p')
  if (element % 2 == 0) {
    p_tag.innerText = `${element} is even number`
    even_number.appendChild(p_tag)
  }
  else {
    p_tag.innerText = `${element} is odd number`
    odd_number.appendChild(p_tag)
  }
})

위 코드는 1~20까지의 배열에 있는 요소(숫자)가 짝수냐 홀수냐에 따라 다른 div에 추가한다. 그리고 아래 코드도 비슷한 일을 하는데, forEach는 21~40까지 있는 배열을 순회하면서 요소(숫자)에 조건문을 적용하고 각각의 div에 추가하게 된다.

See the Pen Untitled by koreanraichu (@koreanraichu) on CodePen.

const button = document.getElementById('button')
const print = document.querySelector('.print')
const choose = document.querySelectorAll('.pokemon')

button.addEventListener('click',(e)=>{
  let question = ''
  choose.forEach(function(element){
    if (element.checked) {
      if (element.value == 'grass') {
        question = '풀타입 포켓몬인 이상해씨로 하겠느냐?'
      }
      else if (element.value == 'water') {
        question = '물타입 포켓몬인 꼬부기로 하겠느냐?'
      }
      else if (element.value == 'fire') {
        question = '불타입 포켓몬인 파이리로 하겠느냐?'
      }
      else {
        question = '전기타입 포켓몬인 피카츄로 하겠느냐?'
      }
      print.innerText = question
    }
  })
})

for나 forEach를 사용하면 이런 식으로 동일한 클래스의 라디오버튼을 배열로 받아오고 순회하면서, 선택이 되었는지를 확인하고 선택된 라디오버튼의 값에 따라 다른 이벤트를 줄 수 있다.