JavaScript
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으로 작용하는 경우
- 중첩 객체 디스트럭처링 가능