20210714 JavaSciprt DeepDive 12 : Symbol, 이터러블(Iterable), 스프레드 문법, 디스트럭처링 할당(구조 분해 할당)
JavaScript Deep Dive 12
📄 용어 및 중요사항 정리
Symbol
심벌
- 변경 불가능한 원시 타입의 값
 - 다른 값과 중복되지 않는 유일무이한 값
 - 이름 충돌 위험이 없는 유일한 프로퍼티 키를 만들기 위해 사용함
 
심벌 값의 생성
Symbol 함수:- Symbol 함수를 호출하여 심벌을 생성함 (다른 원시값의 경우 리터럴 표기법을 통해 값 생성)
 - new 연산자와 함께 호출하지 않음 (인스턴스가 아니라 원시값을 생성하는 것임)
 - 생성된 심벌 값은 외부로 노출되지 않아 확인 불가함
 Symbol(description): Symbol 함수의 인수로는 생성된 심벌값에 대한 설명인 문자열을 전달 가능함- 설명 문자열이 같더라도 언제나 Symbol은 변경 불가한 유일무이한 값을 줌으로서 다름
 
- 생성된 심벌 값에 객체처럼 접근하면 다른 원시 값과 같이 래퍼 객체를 생성함
 - 심벌 값은 암묵적으로 문자열이나 숫자 타입으로 변환되지 않음
        
- 불리언 타입으로는 암묵적 타입 변환이 이루어짐 -> 심벌 값의 존재 확인 가능
 
 
Symbol.for(stringKey): 인수로 전달 받은 문자열을 키로 사용하여 키와 심벌 값의 쌍들이 저장되어 있는 전역 심벌 레지스트리에서 이미 저장된 심벌 값을 검색하거나, 값을 생성함- 전달된 인수(stringKey)가 존재하는 경우, -> 해당 인수에 맞는 심벌 값을 반환
 - 전달된 인수(stringKey)가 존재하지 않는 경우, -> 해당 인수에 맞는 심벌 값을 생성하여 저장하고 반환
 - 모든 곳에서 중복되지 않는 상수 역할의 심벌 값을 관리하여 공유 할 수 있음
 
Symbol.keyFor(symbolValue): Symbol for로 생성된 심벌값을 인수로 전달하면, 전역 심벌 레지스트리에서 해당 심벌값의 생성할 당시의 key를 찾아 반환해줌
- 변경/중복될 가능성이 있는 상수 대신 중복 가능성 없는 심벌을 사용할 수 있음
 
enum: 명명된 숫자 상수의 집합으로 열거형(enumerated type)이라고 함- JS는 enum을 지원하지 않음, TS는 enum을 지원함
 
심벌인 프로퍼티 키:- 객체의 프로퍼티 키를 심벌로 지정가능하고, 동적으로 생성 가능함
 .을 통한 프로퍼티 값 접근은 불가함[]대괄호를 사용하여 안에 심벌 값을 넣어 프로퍼티 값에 접근 가능함- 기존의 프로퍼티 키, 미래에 사용할 프로퍼티 키와 절대로 충돌이 없음
 
const obj = {
  [Symbol.for("mySymbol")]: 1,
};
obj[Symbol.for("mySymbol")]; // 1
심벌인 프로퍼티 은닉- 심벌 값을 프로퍼티 키로 사용한 프로퍼티는 for…in, Object.keys, Object.getOwnPropertyNames 와 같은 일반적인 메서드로는 찾을 수 없음 -> 외부로 부터 은닉이 됨
 - Object.getOwnPropertySymbols 메서드 사용시 해당 심벌 프로퍼티 키를 찾을 수 있음(찾아서 배열로 반환 함)
 - 해당 프로퍼티 키를 찾아 가져와서, 객체에 대괄호[ ]로 접근하여 값을 가져올 수 있음
 
심벌과 표준 빌트인 객체 확장- 표준 빌트인 객체에 사용자 정의 메서드를 직접 추가하면, 나중에 도입되는 메서드와 충돌이 날수 있음
 - 심벌을 통해서 메서드 이름으로 사용하여 추가하면 충돌을 해결 할 수 있음
 
Array.prototype[Symbol.for("sum")] = function () {
  return this.reduce((acc, cur) => acc + cur, 0);
}[(1, 2, 3, 4)][Symbol.for("sum")](); // 10
Well-known Symbol: JS가 기본 제공하는 빌트인 심벌 값- 순회 가능한 빌트인 이터러블은 Symbol.iterator를 키로 갖는 메서드를 가짐
 - Symbol.iterator 메서드 호출시 이터레이터를 반환함
 
이터러블 (Iterable)
이터레이션 프로토콜
이터레이션 프로토콜: 순회 가능한 데이터 컬렉션(자료구조)를 만들기 위해 약속한 규칙(이터러블 프로토콜 + 이터레이터 프로토콜)- 순회 가능한 데이터 컬렉션 : 배열, 문자열, 유사 배열 객체, DOM 컬렉션 등..
        
- ES6 이전 for문, for…in문, forEach등으로 다양한 방법으로 순회 방식
 
 - 이터레이션 프로토콜을 준수하는 이터러블 통일
        
- for … of문, 스프레드 문법, 배열 디스트럭처링 할당의 대상으로 사용 가능
 
 - 데이터 소비자(사용방식)와 데이터 공급자(자료형)를 연결하는 인터페이스 역할 - 순회하는 다양한 자료형에 따라 사용 메서드가 많아지면, 사용에 혼선이 있고 비효율적임 - -> 순회하는 다양한 자료형에 규칙을 두어 규칙을 만족하면, 정해진 사용방식을 통해 다양한 자료형을 사용할 수 있게함
 
- 순회 가능한 데이터 컬렉션 : 배열, 문자열, 유사 배열 객체, DOM 컬렉션 등..
        
 이터러블 프로토콜:- Symbol.iterator를 프로퍼티 키로 사용한 메서드를 직접 구현시, 프로토타입체인을 통해 상속 받은 Symbol.iterator 메서드 호출시 이터레이터 프로토콜을 준수한 이터레이터를 반환하는 이터러블을 사용해야하는 규약
 
이터레이터 프로토콜:- 이터러블의 Symbol.iterator 메서드 호출시 (next 메서드를 소유하고, next 메서드 호출시 이터러블을 순회하며 이터레이터 result 객체를 반환하는) 이터레이터를 사용해야 하는 규약
 
이터러블: Symbol.iterator를 프로퍼티 키로 사용한 메서드를 직접 구현하거나, 프로토타입체인을 통해 상속 받아 Symbol.ierator 참조, 호출시 이터레이터를 반환하는 객체- 배열, 문자열, Map, Set 등은 이터러블
 for...of 문으로 순회 가능,스프레드 문법,배열 디스트럭처링 할당(배열 구조분해 할당)의 대상으로 사용 가능- 스프레드 문법은 일반 객체도 사용 가능
 
이터레이터: next 메서드를 소유하여 next 메서드 호출시 이터러블을 순회하며 매 순회 마다 이터레이터 result 객체를 반환하는 함수객체- 이터러블의 요소를 탐색하기 위한 포인터 역할
 
이터레이터 result 객체:{value: any, done: boolean}- value 프로퍼티 : 현재 순회 중인 이터러블의 값
 - done 프로퍼티 : 이터러블 전체 순회 완료 여부
 
빌트인 이터러블
- 이터레이션 프로토콜을 준수한 객체인 JS 자체(빌트인) 이터러블
 
- 빌트인 이터러블인 표준 빌트인 객체
 
| 빌트인 이터러블 | Symbol.iterator 메서드 | 
|---|---|
| Array | Array.prototype[Symbol.iterator] | 
| String | String.prototype[Symbol.iterator] | 
| Map | Map.prototype[Symbol.iterator] | 
| Set | Set.prototype[Symbol.iterator] | 
| TypeArray | TypeArray.prototype[Symbol.iterator] | 
| arguments | arguments[Symbol.iterator] | 
| DOM 컬렉션 | NodeList.prototype[Symbol.iterator]  HTMLCollection.prototype[Symbol.iterator]  | 
    
for … 문
for (변수선언문 of 이터러블) {}- 이터러블 순회하면서 이터러블의 요소를 변수에 할당
        
- 내부적으로 이터레이터의 next 메서드 호출시 반환되는 result 객체의 value 프로퍼티 값을 변수에 할당하여 순회함
 - result 객체의 done 프로퍼티 값이 true가 되면 이터러블의 순회 중단함
 
 
- 이터러블 순회하면서 이터러블의 요소를 변수에 할당
        
 
for (변수선언문 in 객체) {}- 객체의 프로토타입 체인상에 존재하는 모든 프로토타입 프로퍼티 중에서 [[Enumerable]] 값이 true인 프로퍼티를 순회하며 열거
 - 프로퍼티 키 symbol인 경우는 열거 제외
 
이터러블 vs 유사 배열 객체
- 
    
유사 배열 객체- 인덱스로 프로퍼티 값에 접근, length 프로퍼티를 갖는 객체
 - for문 순회 O / for …of 순회 X
 - Symbol.iterator 없음
 - 이터러블X, 일반 객체O
 - Array.from 메서드로 배열로 반환하여 이터러블로 만들수 있음
 
 - 
    
이터러블인 유사 배열 객체- arguments, NodeList, HTMLCollection
 
 
사용자 정의 이터러블
사용자 정의 이터러블: 이터레이션 프로토콜을 준수하도록 설정한 일반 객체- Symbol.iterator 메서드 구현
        
- 반환되는 이터레이터 구현
            
- 반환되는 result 객체 구현
 
 
 - 반환되는 이터레이터 구현
            
 
- Symbol.iterator 메서드 구현
        
 
// 사용자 정의 이터러블
// 피보나치 수열의 10 이하의 값만 나타내는 이터러블
const fibonacci = {
  // Symbol.iterator 메서드 구현
  [Symbol.iterator]() {
    let [pre, cur] = [0, 1];
    const max = 10;
    // 이터레이터 반환
    return {
      // next 메서드 구현
      next() {
        [pre, cur] = [cur, pre + cur];
        // result 객체
        return { value: cur, done: cur >= max };
      },
    };
  },
};
// for ... of
for (const num of fibonacci) {
  console.log(num); // 1, 2, 3, 5, 8
}
// 스프레드 문법
const arr = [...fibonacci];
console.log(arr); // 1, 2, 3, 5, 8
// 배열 디스턱처링 할당
const [first, second, ...rest] = fibonacci;
console.log(first, second, rest); // 1 2 [3, 5, 8]
이터러블을 생성하는 함수- 필요한 인수를 받아 변하는 이터러블을 생성하는 함수 구현
 
// 이터러블 반환 함수
const fibonacciFunc = function (max) {
  let [pre, cur] = [0, 1];
  // 이터러블 반환
  return {
    // Symbol.iterator 메서드 구현
    [Symbol.iterator]() {
      // 이터레이터 구현
      return {
        // next 메서드 구현
        next() {
          [pre, cur] = [cur, pre + cur];
          // result 객체 반환
          return { value: cur, done: cur >= max };
        },
      };
    },
  };
};
// for ... of
for (const num of fibonacciFunc(10)) {
  console.log(num); // 1 2 3 5 8
}
이터러블이면서 이터레이터인 객체를 생성하는 함수- next 메서드를 활용하고자 하면 이터레이터를 받아와야 함, Symbol.iterator 메서드를 호출해서 이터레이터를 받아와야함
 - 그럴 필요 없이 이터러블에서 바로 next 메서드를 만들어 사용할수 있게 함
 
const fibonacciFunc = function (max) {
  let [pre, cur] = [0, 1];
  // 이터러블
  return {
    [Symbol.iterator]() {
      return this; // this는 이터러블
    },
    next() {
      [pre, cur] = [cur, pre + cur];
      return { value: cur, done: cur >= max };
    },
  };
};
let iter = fibonacciFunc(10);
for (const num of iter) {
  console.log(num); // 1 2 3 5 8
}
iter = fibonacciFunc(10);
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: 5, done: false}
console.log(iter.next()); // {value: 8, done: true}
무한 이터러블- result 객체의 done 프로퍼티를 명시하지 않은 경우
 - 무한으로 값을 생성하여 가져올 수 있음(필요한 만큼만 for…of, 배열 디스트럭처링 할당에서 결정하여 가져옴)
 
지연 평가: 데이터가 필요한 시점 이전까지는 미리 데이터를 생성하지 않고 필요한 시점이 되면 그때 데이터를 생성하는 기법- 평가 결과가 필요할때(for…of, 배열 디스트럭처링 할당) 까지 평가를 늦춤
 - for…of문의 경우 이터레이터 next 메서드 호출할 때 데이터가 생성됨
 
스프레드 문법 (spread syntax, 전개 문법)
- 하나로 뭉쳐 있는 여러 값들의 집합을 펼쳐서 개별적인 값들의 목록으로 만듦
 - 사용 가능 대상
    
- 이터러블에 한정
 - Array, String, Map, Set, DOM 컬렉션(NodeList, HTMLCollection), arguments
 
 - 스프레드 문법의 결과는 값이 아니라 값의 목록 임
 - 스프레드 문법의 결과는 변수에 바로 할당 할수 없음
 - 값 목록을 사용하는 문맥
    
- 함수 호출문의 인수 목록
 - 배열 리터럴의 요소 목록
 - 객체 리터럴의 프로퍼티 목록
 
 
함수 호출문의 인수 목록에서 사용
- 함수 호출문에서 인수를 전달할 때 사용하는 인수 목록으로 활용 가능함
    
- 스프레드 문법 전에는 
apply를 통해서 인수 배열을 제공하여 인수를 전달 했음 
 - 스프레드 문법 전에는 
 - rest 파라미터와 반대의 개념
    
- rest 파라미터 : 인수 목록 -> 배열
 - spread 문법 : 배열 -> 인수 목록
 
 
const arr = [1, 2, 3, 4, 5];
const applyMax = Math.max.apply(null, arr); // 5
const spreadMax = Math.max(...arr); // 5
배열 리터럴 내부에서 사용
- 
    
2개의 배열을 하나로 합치고 싶은 경우
- 과거 : 
Array.prototype.concat(arr) - spread 문법 방식 : 
[...Array1, ...Array2] 
 - 과거 : 
 - 
    
Array1을 Array2에 특정한 위치에 넣고 싶은 경우
- 과거 : splice + apply + concat
 - spread 방식 : splice에 3번 째 인자 부터 풀어서 넣음
 
 - 
    
배열 복사를 하고 싶은 경우
- 과거 : slice함수 호출
 - spread 방식 : [] 빈 배열에 넣어 풀어사용
 
 - 
    
이터러블, 유사배열 객체를 배열로 변환하고 싶은 경우
- 과거 : slice + call or Array.from
 - spread 방식 : [] 빈 배열에 넣어 풀어사용
        
- 단, 이터러블이 아닌 유사배열 객체는 불가
 - from은 이터러블이 아닌 유사배열 객체도 가능
 
 
 
객체 리터럴 내부에서 사용
- 스프레드 프로퍼티를 사용하면 객체 리터럴의 목록에서도 스프레드 문법 사용 가능
    
- 일반 객체 대상으로도 스프레드 문법 사용 허용
 복사 또는 병합으로 많이 사용- 과거: Object.assign 메서드를 통해서 병합, 변경하였음
 - spread 방식 : 빈 객체{}에 풀어서 넣어주면 됨
 
 
디스트럭처링 할당 (구조 분해 할당)
- 배열과 같은 이터러블 또는 객체를 구조 파괴하여 1개 이상의 변수에 개별적으로 할당하는 것을 말함
 
배열 디스트럭처링 할당
할당의 대상(우변): 이터러블 (이터러블이 아니면, TypeError 발생)할당 기준: 배열의 인덱스 (순서대로 할당됨)- 변수의 개수와 이터러블의 요소 개수가 반드시 일치할 필요는 없음
 - 변수의 개수 > 이터러블의 요소 개수 -> 할당 받지는 못한 변수는 undefined 임
 
할당 받을 변수(좌변): 배열 리터럴 형태로 선언- let을 사용하면 변수 선언과 할당을 분리할 수 있음 (비추천)
 - 할당 받을 변수에 기본값 설정 가능 (기본값 보다 할당값이 우선됨)
 - 할당 받을 변수에 Rest 요소(…Rest)를 사용 가능
        
- 남은 값의 배열을 이루는 변수가 됨
 - 항상 마지막에 위치해야 함
 
 
객체 디스트럭처링 할당
할당의 대상(우변): 객체 (또는 객체로 평가 될수 있는 표현식인 문자열, 숫자, 배열 등…)할당 기준: 프로퍼티 키- 순서 상관없이, 선언된 변수 이름과 프로퍼티 키가 일치하면 할당 됨
 
할당 받을 변수(좌변): 객체 리터럴 형태로 선언- 객체의 프로퍼티 키와 다른 변수 이름으로 프로퍼티 값을 할당 받기 가능
        
- 좌변의 프로퍼티 키에 프로퍼티 값으로 사용하고 싶은 이름을 넣으면 됨
 
 - 할당 받을 변수에 기본값 설정 가능
 - 함수의 매개변수에도 사용 가능
 - 할당 받을 변수에 Rest 요소(…Rest)를 사용 가능
        
- 남은 프로퍼티를 가진 객체를 이루는 변수가 됨
 
 
- 객체의 프로퍼티 키와 다른 변수 이름으로 프로퍼티 값을 할당 받기 가능
        
 
- 배열과 객체 디스트럭처링을 같이 사용 가능
    
- 배열안에 객체가 item으로 작용하는 경우
 
 - 중첩 객체 디스트럭처링 가능