JS - for in / for of 란?

김지현·2021년 5월 31일
3

Javascript

목록 보기
1/3
post-thumbnail

for ~in / for ~of 구문의 차이?

✔️ 자바스크립트를 통해 여러 로직들을 구현하다 보면, 객체를 생성하는 경우가 아주 많다. 더불어, 생성한 객체 내의 값들을 순회하며 출력하기도 한다.

for in과 for of를 구분하는 가장 큰 차이는 바로 '순회하는 대상' 이다.
두 구문 모두 순회한다는 의미는 같으나, 적용 대상이 달라진다.

예시를 들어보기에 앞서 두 구문의 차이에 대해 먼저 명시해보려 한다.


🚀 <for~in>

  • 객체의 열거 가능한 속성 값에 접근하면서 순회
  • 객체의 key에 접근 가능(객체의 경우 index). value에도 접근은 가능하나 직접적으로는 불가능
  • 모든 객체 유형에서 사용 가능

🚀 <for ~of>

  • Iterable(반복 가능한) 객체의 값을 순회
  • 객체의 key에 접근 불가능. value에 접근
  • Object에서는 사용 불가능

✍️ 참고 사항: 여기서 언급하는 Object !== 객체이다. Object는 말 그대로 Object이고, 객체는 함수, 배열, object 등을 모두 아우르는 단어이다.

또한, 객체는 Key와 Value로 이루어져 있다. 배열에서의 Key란 index 값이며, Value는 각 배열에 들어 있는 값이라고 생각하면 된다.


for ~in

for in문은 객체의 모든 열거 가능한 속성들을 순회하도록 해준다.
여기서 순회 가능한 속성이라는 것은 객체의 key 값을 말한다.

value 값은 직접 접근하는 것이 불가능하므로, 만약 객체의 value 값을 알고 싶다면 객체[key]와 같은 표현 방식을 통해 간접적으로 접근해야 한다.

바로 예시를 통해 살펴보자.

// Object 생성
const introduce = {
  name: 'gil dong',
  age: 25,
  city: 'Seoul',
  hobby: 'movie',
}

// Array 생성
const person = ['길동', '길서', '길북', '길남'];

for (let key in introduce) {
  console.log(key); // 'name','age','city','hobby'
  console.log(introduce[key]); // 'gil dong', 25,'Seoul','movie'
}

for (let myKey in person) {
  console.log(myKey); // '0','1','2','3'
  console.log(person[myKey]); // '길동','길서','길북','길남'
}

위 예시 코드들을 보면,

for ~in문을 통해 각각 객체와 배열의 key에 접근했다.
그리고 introduce[key], person[myKey]와 같이 key를 통해서 간접적으로 value에 접근했다.
이처럼 for ~in문을 사용하면 객체의 값에 접근하여 순회하는 것이 가능하다.

📍 하지만, 주의해야 할 점도 분명 있다!

위에서도 설명했듯, for ~in문은 순서가 없는 객체에 임의의 순서를 가지고 접근하는 방식이다.

✔️ 따라서 순서대로, 정렬된 값을 출력해야 하는 경우에는 for ~in문을 사용하지 않는 것이 좋겠다. 특히나 반복 가능하며 순서를 가지는 배열의 경우, for ~in문 보다는 후에 설명할 for ~of문을 사용하는 것이 더 적절하다.


for ~of

for ~of문은 반복 가능한(iterable)한 값을 순회하는 데 사용된다.
따라서 순서가 없고 반복 불가능한 Object의 경우에는 사용이 불가하다.

for ~in문이 객체의 모든 열거 가능한 속성들을 대상으로 순회하는 것과는 다르게, for ~of문은 [Symbol.iterator] 속성을 가지는 것들만을 대상으로 한다. 또한, for ~of문은 배열의 경우 index 값과 같이 대상의 value가 아닌 key에 접근하는 것이 불가능하다.

바로 예시를 들어 살펴보자.


const introduce = {
  name: 'gil dong',
  age: 25,
  city: 'Seoul',
  hobby: 'movie',
}

const person = ['길동', '길서', '길북', '길남'];

// 배열에서의 key: '0', '1' 등의 인덱스 값
// 배열에서의 value: '길동', '길서' 등의 값

for (let thisKey of person) {
  console.log(thisKey); // '길동','길서','길북','길남'
  console.log(person[thisKey]); // undefined 
}

for (let hereKey of introduce) {
  console.log(hereKey); // TypeError: introduce is not iterable
}

극명한 차이를 보기 위해 객체와 배열 모두에 for ~of 문을 적용해보았다.

✔️ for ~of 문을 통해 배열에 접근 시, value 값은 정상적으로 잘 출력된다.
하지만 person[thisKey]를 통해 배열의 key값에 접근하려고 하니 undefined가 뜨는 것을 알 수 있다.

이는 즉, for ~of 문을 통해서 객체의 key에 접근하는 것은 직, 간접적으로 모두 불가능하다는 것을 의미한다.

✔️ 이번에는 Object에 for ~of 문을 통해 접근한 것을 살펴 보자.

console.log(hereKey)를 통해 출력해보니, 'introduce'는 반복문이 아니라는 에러가 뜨는 것을 알 수 있다. 즉, 이를 통해 유추할 수 있는 것은 Object는 iterable 하지 않고, 따라서 for ~of로 접근 불가능하다.


📌 정리하면,

for ~in 문은 객체의 'key'를 순회하고, for ~of 문은 iterable 객체 (배열 등)의 'value'를 순회하는 데 사용된다.

이러한 차이점을 잘 활용하여 효율적으로 코드를 작성하는 것이 좋겠다.


🧷 JS - REPLIT 30번 회고

레플릿을 풀면서 내가 가장 헤맸던 30번에 대해서 다시 정리하며 정리하고자 한다.

문제는 이러하다.

🆀 아래 설명을 읽고 getExamResult 함수를 구현하세요.

인자 scores 는 다음과 같은 객체입니다. 객체의 요소의 갯수 및 키의 이름들은 달라질 수 있습니다. 객체의 값은 다음 9가지 문자열 중에서 하나를 가지고 있습니다.

'A+', 'A', 'B+', 'B', 'C+', 'C', 'D+', 'D', 'F'
{
    '생활속의회계': 'C',
    '논리적글쓰기': 'B',
    '독일문화의이해': 'B+',
    '기초수학': 'D+',
    '영어회화': 'C+',
    '인지발달심리학': 'A+',
}

인자 requiredClasses 는 다음과 같이 문자열로 된 배열입니다.

['영어회화', '기초수학', '공학수학', '컴퓨터과학개론']

다음 조건을 만족하는 객체를 리턴하도록 함수를 구현해주세요.

scores 객체가 가지고 있는 키들은 새로운 객체에 포함되어야 합니다. 단, 그 값들은 다음 원리에 따라 숫자로 바뀌어 할당되어야 합니다.

A+ => 4.5
A => 4
B+ => 3.5
B => 3
C+ => 2.5
C => 2
D+ => 1.5
D => 1
F => 0

requiredClasses 배열의 요소로는 존재하지만, scores의 키로는 존재하지 않는 항목이 있다면, 해당 요소는 새로운 객체의 키가 되고, 값으로 0을 가져야 합니다. 위에서 예시로 묘사된 객체와 배열이 인자로 들어왔다면, 다음과 같은 객체과 리턴됩니다. 요소간 순서는 다를수 있지만, 채점에 무관합니다.

{
  '생활속의회계': 2,
  '논리적글쓰기': 3,
  '독일문화의이해': 3.5,
  '기초수학': 1.5,
  '영어회화': 2.5,
  '인지발달심리학': 4.5,
  '공학수학': 0,
  '컴퓨터과학개론': 0,
}

📍 문제 풀이 접근 과정

0️⃣ 새로운 빈 객체를 하나 만들자. 최종적으로 이 객체를 return 할 예정.
1️⃣ 영어 점수와 숫자 점수를 담은 객체를 먼저 생성. 가령 이름은 newObject라고 할 때,
2️⃣ scores 객체의 value와, newObject 객체의 key를 비교하자.
3️⃣ 만약 두 값이 같다면, 빈 객체의 value로 할당하자.
4️⃣ scores 객체의 key와 requiredClasses 배열의 value를 비교하자.
5️⃣ 만약 배열에는 값으로는 있으나, 객체에는 없는 값이 있다면 그 값은 0으로 할당하자.

위 과정을 나열하여 코드로 작성해보면,


const getExamResult = (scores, requiredClasses) => {
  const result = {}; 

  let myObject = {
    'A+' : 4.5, 
    'A' : 4,
    'B+' : 3.5,
    'B' : 3,
    'C+' : 2.5,
    'C' : 2,
    'D+' : 1.5,
    'D' : 1,
    'F' : 0
  };
  
  // object 순회
  for (let key in scores) {
    for (let myKey in myObject){
      if (scores[key] === myKey) {
        result[key] = myObject[myKey];
      } 
    };
  }

  // array 순회
  for (let elements of requiredClasses) {
    if (scores[elements] === undefined) {
      result[elements] = 0;
    }
  }

  return result;
}

이렇게 구현 가능하다! 그런데 뭔가 비효율적이라는 생각이 들었다.

굳이 두 번째 for ~of 문에서 undefined과 비교해서 조건문으로 넣는 것보다, 차라리 먼저 할당해준다음 값을 비교해서 넣는 건 어떨까? 🧐 하는 생각이 들었다.

그래서,


// array 먼저 순회 및 0을 할당
  for (let key of requiredClasses) {
      result[key] = 0; 
    }

// object 순회 및 재할당
  for (let keys in scores) {
    let secondKey = scores[keys];
    let myObjectValue = myObject[secondKey]; 
    result[keys] = myObjectValue; 
  }

위와 같이 구현해보았다.

바뀐 점은, 우선 배열을 순회하는 코드가 위로 가고 객체를 순회하는 코드는 밑으로 왔다.

그 이유는 우선 첫 번째 코드처럼 비교를 하는 것이 아니라, 우선 result라는 새로운 객체의 value로 0을 모두 할당 해둔 뒤, result의 value에 myObject라는 객체의 value를 재할당 해줌으로서 배열에는 있고, 객체에는 없는 값이 있다면 그것의 value만 0이 되도록 해준 것이다!

따라서 배열 순회 코드가 먼저 오고, 그 다음에 객체 순회 코드가 온 것이다.
이렇게 작성한 코드가 코드의 길이도 짧고, 작동 로직도 구성해서 훨씬 효율적이라고 생각한다... ? 🥸

아무튼 한참을 고민하고 고민해서 위 두 가지 방법을 고안 해냈다.
이게 맞는 방법인지, 정답인지는 아직 확신은 못하겠지만 그래도 해냈다는 것이 뿌듯하고.. 나에게서 정말 많은 시간을 빼앗아 간 문제였다 😢😭😰


✨ TIL을 마무리하며

Javascript - replit 30번을 풀면서 이 두 가지 구문을 사용하게 되었는데, 한 번 더 그 기능과 특징에 대해 정리하고 싶어서 이번 주제를 for in/for of로 잡게 되었다.

헷갈리기 쉬운 만큼, 글을 작성하면서 다시금 완벽 소화할 수 있기 위해 많은 노력을 기울였다...!! 자주 유용하게 사용되는 구문이니 만큼, 확실히 익혀두자!

profile
나만의 세상을 개척하고 싶은 사람

0개의 댓글