[Javascript] 요소 활용 총정리 ① | 이터러블, 스프레드, 디스트럭처링

Re_Go·2024년 1월 1일
0

Javascript

목록 보기
33/44
post-thumbnail

1. 이터러블의 의미

ES6부터 이터러이션 프로토콜 사양이 확립되어 반복적으로 순회 가능한 개체들을 확립했는데요. 이들의 특징은 다음과 같습니다.

  1. Symbol.iterator 메서드: 이터러블 객체들은 각각의 고유한 심볼 Symbol.iterator 메서드를 구현하여 next메서드가 포함된 반복자(iterator)를 반환하는데, 이 next 메서드를 통해 각 객체의 요소들을 순회하면서 done의 값을 false를 반환하다가, 최종적으로 처리 할 요소가 없을 때 최종적으로 done의 값을 true로 반환하면서 해당 이터러블에 대한 순회를 종료시킵니다.
const iterableObject = {
  data: [1, 2, 3], // 전달할 데이터 세팅.
  [Symbol.iterator]: function () {
    let index = 0; // 함수 내부에서 index를 0으로 선언 후

    return { // next 메서드를 반환하하는데
      next: () => {
        if (index < this.data.length) { // 현재 인덱스의 값이 전달 받은 데이터의 길이보다 작을 경우, 즉 아직 배열을 순회하는 동안인 경우
          return { value: this.data[index++], done: false }; // Value에는 data의 다음 인덱스 값을, done의 상태는 false인 객체를 반환
        } else {
          return { value: undefined, done: true }; // 인덱스의 값이 데이터의 길이와 같은 경우, 즉 해당 작업을 끝낸 경우 value를 undefined로 초기화 하고 done의 상태를 true로 할당하여 순회 작업을 종료시킵니다. 
        }
      },
    };
  },
};

const iterator = iterableObject[Symbol.iterator]();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
  1. 내장 이터러블 객체: JavaScript에는 이미 이터러블을 구현한 내장 객체들이 있습니다. 배열(Array), 문자열(String), 맵(Map), 셋(Set) 등은 기본적으로 이터러블 객체입니다. 반면에 일반 객체는 생성시 내장 이터러블 객체가 존재하지 않으므로, 이터러블 메서드와 구조를 활용하려면 해당 내장 객체들을 생성해야 합니다.
const isIterable = object => object !== null && typeof object[Symbol.iterator] === 'function' ? 'iterable' : 'not-iterable';
// 해당 객체가 비어있지 않은 동시에 Symbol.iterator 객체가 함수(메서드) 인 경우의 여부로 이터러블인지, 아닌지를 반환

console.log(isIterable([])); // iterable
console.log(isIterable('')); // iterable
console.log(isIterable(new Map())); // iterable
console.log(isIterable(new Set())); // iterable
console.log(isIterable({})); // not-iterable
  1. 반복 구조에서 사용 가능: 이터러블은 for...of 루프, Array.from(), spread 연산자 등과 같은 반복 구조에서 직접 사용할 수 있습니다. 일반 객체는 이러한 구조에서 사용할 수 없습니다.
// 1. for-of로 순회 가능
const iterableArray = [1, 2, 3];

for (const item of iterableArray) {
  console.log(item);
}


// 2. 이터러블 객체를 Array.from 메서드를 이용해 다른 변수에 반환 가능
const iterableSet = new Set([1, 2, 3]);
const arrayFromSet = Array.from(iterableSet);
console.log(arrayFromSet); // [1, 2, 3]


// 3. 전개 연산자(스프레드)를 사용해 각 요소가 구분된 배열을 반환 할 수 있습니다.
const iterableString = 'Hello';
const arrayFromString = [...iterableString];
console.log(arrayFromString); // ['H', 'e', 'l', 'l', 'o']


// 4. 이터러블에서 구조 분해 할당(Destructuring)을 이용해 원하는 데이터를 추출하여 할당할 수 있습니다.
const iterableArray = [1, 2, 3];
const [first, second, third] = iterableArray;
console.log(first, second, third); // 1 2 3

2. 스프레드 연산자

ES6부터 도입된 스프레드 연산자는 자바스크립트에서 구현된 이터러블 뿐만 아니라 undefined와 null 객체를 제외한 모든 객체가 사용이 가능한데요. 이러한 스프레드 연산자의 역할은 주로 이터러블 안의 요소들을 개별적으로 구분하여 배열 형태로 재반환 하는 것을 의미하는데,

이 연산자의 필요성은 메서드의 복사하거나 두 개 이상의 배열을 합치는 역할도 있겠으나, 해당 배열의 요소들을 개별적으로 하나 하나 전달해야 하는 역할을 주로 사용되고 있습니다.

언뜻 보면 이러한 작업들은 다른 메서드들로 대체될 수 있는 작업임을 의미하기도 하지만, 그만큼 이러한 배열의 복사나 분리에 concat, splice, push등과 같은 메서드를 활용할 만큼 생각외로 번거로움 작업이었음을 의미하기도 하는 만큼, 스프레드 연산자의 등장은 이전 메서드들을 활용한 배열의 복사 및 요소 전개의 역할을 대체하기에 충분하다고 할 수 있겠습니다.

이러한 스프레드 연산자는 대괄호(배열 반환), 중괄호(객체 반환), 소괄호(인자 전달 및 배개변수 할당) 안에 전개할 대상을 작성하는 것으로 사용이 가능합니다.

const arr1 = [1, 2, 3];
const arr2 = ['일', '이', '삼'];

// 두 배열을 합치기
const mix = [...arr1, ...arr2];
console.log(mix); // [1, 2, 3, '일', '이', '삼']

// 배열의 복사
const copy = ...arr1;
console.log(copy); // [1, 2, 3]
// 객체의 얕은 복사 
const obj = {first : 1, second : 2, third : 3}
const copyObj = {...obj}
console.log(obj === copyObj) // 얕은 복사 구현으로 구조는 같지만 메모리 주소가 다르므로 false를 반환합니다.

⭐ 스프레드 연산자를 이용한 객체간의 병합시 키의 값이 같을 경우 뒤쪽에 객체의 키값으로 덮어씌어지는 섀도잉 현상을 확인할 수 있습니다.

3. 디스트럭처링(Destructuring)

말 그대로 데이터의 구조를 분해한 후 개별적인 변수들에 하나씩 할당하는 것을 의미합나. 이 방법이 등장하기 전까지는 각각 객체의 요소들을 하나씩 추출하여 일일이 할당을 해야만 했지만, ES6 부터 등장한 이후부터는 일일이 할당하는 코드의 작성을 덜어낼 수 있었습니다.

특히 배열의 요소가 객체인 경우 디스트럭처링의 단짝과도 같은 존재이기 때문에 유용히 사용되는 방법입니다.

// 배열 요소에 대한 분해의 기존 방식
const numbers = [1, 2, 3];
const firstNumber = numbers[0];
const secondNumber = numbers[1];
const thirdNumber = numbers[2];

// 디스트럭처링 등장 이후
const [first, second, third] = numbers;

console.log(first); // 1
console.log(second); // 2
console.log(third); // 3
// 객체의 분해에 대한 기존 방식
const person = { name: 'John', age: 30 };
const name = person.name;
const age = person.age;

// 디스트럭처링 방식
const { name, age } = person;

console.log(name); // 'John'
console.log(age); // 30
// 배열 안 객체 요소들에 대한 간단한 디스트럭처링 활용 예시
const array = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Alice' },
  { id: 4, name: 'Thomas' },
  { id: 5, name: 'Jane' },
];

let aliceCount = 0;
let otherPeoples = 0
for (const { id, name } of array) {

    if(name === 'Alice')  aliceCount++  	
    else otherPeoples ++
  
}
console.log(aliceCount, otherPeoples);
profile
인생은 본인의 삶을 곱씹어보는 R과 타인의 삶을 배워 나아가는 L의 연속이다.

0개의 댓글