1. 심벌이란?
일반적으로 심볼 타입은
객체의 프로퍼티 키를 고유하게 설정함으로써 프로퍼티 키의 충돌을 방지하기 위해 사용된다.
- ES6에서 도입된 7번째 데이터타입
- -변경 불가능한 원시 타입의 값
- -다른 값과 중복되지 않은 유일무이한 값이다
- -주로 이름의 충돌 위험이 없는 유일한 프로퍼티 값을 만들기 위해 사용한다.
2. 생성은 어떻게 하지?
-> Symbol 함수를 호출하여 생성한다. 이때 매번 고유한 객체가 생성이 된다.
const sym1 = Symbol('test');
const sym2 = Symbol('test');
console.log(sym1 === sym2); // false
이렇게만 보면... 생성자 함수로 객체를 생성하는 것처럼 보이지만
다른 생성자 함수와 달리 new 연산자와 함께 호출하지 않는다ㅣ.
new 연산자를 이용하여 래퍼 객체를 생성하려고 하면 TypeError가 발생한다. new 연산자를 이용할 수 없다는 것은 곧 Symbol 함수를 생성자로 사용할 수 없음을 의미한다.
const sym = new Symbol(); // Uncaught TypeError
다른 생성자 함수의 경우 new 연산자를 이용한 래퍼 객체의 생성이 가능하다.
이렇게 생성되는 래퍼 객체는 해당 타입의 원시 값을 저장하고 있고, 유용한 몇몇 메소드들을 가지고 있다.
만약 new 연산자를 이용하지 않고 단순히 Number, String, Boolean 함수를 호출하기만 하면 해당 타입의 원시 값이 생성되기만 하고 래퍼 객체는 생성되지 않는다.
물론 심벌값도 객체처럼 접근하면 암묵적으로 래퍼객체를 생성한다.
하지만 심벌값은 변경 불가능한 원시값이다.
심벌값은 암묵적으로 문자열이나 숫자 타입으로 변환되지 않지만,
불리언 타입으로는 암묵적으로 타입이 변환된다.
const mySymbol = Symbol();
cosole.log(mySymbol + '');
//typeError
//
console.log(+mySymbol);
//typeError
console.log(!!mySymbol);
//true;
심볼도 프로퍼티 키로 사용될 수 있다는 것이다. 이 경우, 기본적으로 심볼은 고유하기 때문에 심볼을 키로 갖는 프로퍼티는 다른 어떤 프로퍼티와도 충돌하지 않을 것이다.
3. symbol for / Symbol.keyFor 메서드
Symbol 함수를 호출함으로써 생성하는 심볼들은 키를 가지고 있지 않으며, 전역 심볼 레지스트리에 저장되지도 않는다.
전역 심볼 레지스트리란 심볼들이 저장되는 전역 공간을 의미하는 것이며, 여러 모듈들이 하나의 심볼을 공유하기 위한 용도로 존재한다.
여러 모듈들이 하나의 심볼을 공유하려면 그 심볼들이 키를 가지고 있어야 한다.
그래야 키를 통해 이미 존재하는 심볼들을 찾아 재활용할 수 있기 때문이다.
인수로 전달받은 문자열을 키로 사용하여 키와 심벌 값의 쌍들이 저장되어 있는 전역 심벌 에서 해당 키와 일치하는 심벌 값을 검색한다.
symbol.for : 유일무이한 상수인 심벌 값은 단 하나만 생성하여 전역 심벌 레지스트리를 통해 공유할 수 있다.
-> 어떻게 ?ㅡ
인자로 전달받은 문자열 값을 키로 갖는 심돌을 전역 심볼 레지스트리에서 찾아 반환하고, 탐색에실패한다면 그 문자열 값을 키로 갖는 심볼을 새로 생성하여 전역 심볼 레지스트리에 저장한 뒤 이를 반환한다.
symbol.keyFor : 전역 심벌 레지스트리에 저장된 심벌 값의 키 추출 가능함
-> 어떻게 ? ㅡ
인자로 전달받은 심볼을 전역 심볼 레지스트리에서 찾고, 그 심볼의 키를 반환하고, 탐색에 실패한다면 undefined를 반환
const sym1 = Symbol.for('foo');
const sym2 = Symbol.for('foo');
console.log(sym1 === sym2); // true
const unsharedSym = Symbol('foo');
const symKey1 = Symbol.keyFor(unsharedSym);
console.log(symKey1); // undefined
const sharedSym = Symbol.for('foo');
const symKey2 = Symbol.keyFor(sharedSym);
console.log(symKey2); // foo
4. 심벌과 상수
상수값은 변경될수도 있고 다른 변수 값과 중복될수도 있다.,
따라서 이러한 무의미한 상수 대신 중복될 가능성이 없는 유일무이한 심벌 값을 사용하도록 하자.
const Direction = {
UP:1,
DOWN:2,
LEFT:3,
RIGHT:4
;
//심볼값으로 정의
const Direction = {
UP:Symbol('up'),
DOWN:Symbol('down'),
LEFT:Symbol('left'),,
RIGHT:Symbol('right'),
;
const myDirection = Direction.UP;
5. 심벌과 프로퍼티 은닉
객체의 프로퍼티 키는 빈 문자열을 포함하는 모든 문자열이나 심벌값으로 만들 수 있으며
동적으로 생성할수도 있다.
심벌 값으로 프로퍼티 키를 동적 생성하여 프로퍼티를 맍들려면 심벌 값에 대괄호를 사용하면 된다. -> 접근할 때도 대괄호로 !
심벌값은 유일무이한 값이기 때문에 심벌 값으로 프로퍼티 키를 만들면 다른 프로퍼티 키와 절대 충돌하지 않고 미래에 추가될 어떤 프로퍼티 키와도 충돌할 위험이 없다.
const obj = {
[Symbol('mySymbol')]:1
};
for (const key in obj) {
console.log(key);
}
//아무것도 출력되지 않음...
심볼 값을 프로퍼티 키로 사용하면 for ... in 이나 Object.getOwnPropertyNames 메서도로 찾을 수 없다.
따라서 외부에 노출할 필요가 없는 프로퍼티를 은닉할 수 있다.
단, ES6에 도입된 Object.getOwnPropertySymbols 메서드를 사용하면 심볼 값을 프로퍼티 키로 사용하여 생성한 프로퍼티를 찾을 수 있다.
6. 빌트인 객체 확장
빌트인 객체에 사용자 정의 메서드를 직접 추가할 때, 다음 ECMAScript에 똑같은 이름의 메서드가 도입된다면 문제가 된다.
따라서 중복될 가능성이 없는 심볼 값으로 빌트인 객체를 확장하면 나중에 추가될지 모르는 어떤 메서드와도 충돌할 위험이 없어 안전하게 빌트인 객체를 확장할 수 있다.
String.prototype[Symbol.for('caseInsensitiveSearch')] = function (target) {
return this.toLowerCase().indexOf(target);
}
`aaaBar`[Symbol.for('caseInsensitiveSearch')]('bar'); // 3
7. well-known Symbol
Well-Known Symbol은 자바스크립트 기본으로 제공하는 표준 빌트인 심볼 값임
이 값은 자바스크립트 엔진 내부 알고리즘에 사용된다. 순회 가능한 빌트인 이터러블은 Well-Known Symbol인 Symbol.iterator를 키로 갖는 메서드를 가지며, Symbol.iterator 메서드를 호출하면 이터레이터를 반환하도록 ECMAScript 사양에 규정되어 있다.
빌트인 이터러블은 이터레이션 프로토콜을 준수한다.
->> 만약 빌트인 이터러블이 아닌 일반 객체를 이터러블처럼 동작하도록 구현하고 싶으면 이터레이션 프로토콜을 따르면 된다. 즉, ECMAScript 사양에 규정되어 있는 대로 Well-known Symbol인 Symbol.iterator를 키로 갖는 메서드를 객체에 추가하고 이터레이터를 반환하도록 구현하면 그 객체는 이터러블이 된다.
'Deep Dive 정리' 카테고리의 다른 글
[JS Deep Dive] 35장 - 스프레드 문법 (0) | 2024.11.14 |
---|---|
[JS Deep Dive] 34장 - 이터러블 (0) | 2024.11.14 |
[JS Deep Dive] 30장 - Date (1) | 2024.11.07 |
[JS Deep Dive] 29장 - Math (2) | 2024.11.07 |
[JS Deep Dive] 28장 - Number (0) | 2024.11.07 |