[JS]모자딥 CH-33 Symbol

현우.·2024년 12월 1일
post-thumbnail

Symbol?

심벌(Symbol)은 ES6에서 도입된 7번째 타입으로 변경이 불가능한 원시값이다.
심벌은 다른 값과 중복되지 않는 특징이 있어 주로 객체의 프로퍼티 키를 만드는데 사용되며
이름 충돌의 위험을 없앨 수 있다.

이름 충돌 예시

const obj = {
  key: 'value1',
};

// 누군가 같은 이름의 키를 추가
obj.key = 'value2';

console.log(obj.key); // 'value2' - 기존 값이 덮어쓰여짐

객체에서 프로퍼티 키로 문자열을 사용할 때, 같은 이름의 키가 추가되면 기존 값이 덮어쓰여질 수 있는
문제가 생길 수 있다.

Symbol 생성

Symbol 함수

심벌 값은 Symbol 함수를 호출하여 생성할 수 있다.
그리고 생성한 심벌 값은 외부로 노출되지 않아 확인할 수 없다.

const mySymbol = Symbol();
console.log(typeof mySymbol); // symbol

console.log(mySymbol); // Symbol()

mySymbol을 호출하면 Symbol() 형식으로 보이지만, 그 뒤에 실제 심볼의 고유 값은 표시되지 않는다.
즉, Symbol()은 심볼의 고유 값을 확인할 수 없는 방식으로 출력되는 것이다.

new 연산자와 함께 호출 불가능

Symbol 함수는 new 연산자와 함께 호출하여 객체(인스턴스)를 만들수 없다.
즉, new 연산자와 호출할 경우 타입에러가 발생한다.

왜냐하면 Symbol은 생성자 함수가 아니고 그저 원시 타입을 생성하는 함수로 사용되기 때문이다.

const mySymbol = new Symbol();

console.log(mySymbol); // TypeError: Symbol is not a constructor

인수에 문자열 전달(description)

Symbol 함수의 인자에는 문자열을 전달할 수 있다.
문자열은 함수에 대한 설명일 뿐, 심볼의 고유값을 변경하거나 심볼 자체의 동작에 영향을 미치지 않는다.

const mySymbol = Symbol("hello");
const mySymbol2 = Symbol("hello");

console.log(mySymbol === mySymbol2); // false

설명의 역할
1. 디버깅: 개발 중에 심벌을 출력하거나 로그에 찍을 때, 그 심벌이 무엇을 나타내는지 알 수 있도록 돕는다. 예를 들어, 심벌의 설명을 통해 해당 심벌이 어떤 기능을 위한 것인지 확인할 수 있다.
2. 디버깅을 위한 문자열: 심벌을 로그에 출력할 때 설명이 표시되므로, 코드에서 심벌을 추적할 때 도움이 된다.

const sym = Symbol("uniqueKey");

console.log(sym); // Symbol(uniqueKey)
console.log(sym.description); // "uniqueKey"

래퍼 객체 생성

심벌 값도 객체처럼 접근시 암묵적으로 래퍼 객체를 생성한다.

const mySymbol = Symbol("hello");

console.log(mySymbol.description); // hello
console.log(mySymbol.toString()); // Symbol(hello)

심벌 값의 암묵적인 타입 변환

심벌 값은 숫자타입이나 문자타입으로 암묵적으로 타입 변환되지 않는다.
그러나 불리언 타입으로는 암묵적으로 타입 변환된다.

const mySymbol = Symbol("hello");

console.log(mySymbol + 1); // error
console.log(mySymbol + " "); // error
console.log(!!mySymbol); // true

Symbol.for, Symbol.keyFor 메서드

Symbol.for(key)

  • 전달받은 문자열을 key로 사용해 전역 심볼 레지스트리에서 심볼을 검색한다.
  • 해당 key의 심볼이 없으면 새로 생성해서 등록한 후 반환, 있으면 기존 심볼을 반환한다.
const sym1 = Symbol.for("shared");
const sym2 = Symbol.for("shared");
const sym3 = Symbol("shared")

console.log(sym1 === sym2); // true(같은 심볼을 반환)
console.log(sym1 === sym3); // false(서로 다른 심볼)

🚀 전역 심벌 레지스트리

전역 심볼 레지스트리는 심볼 값을 전역적으로 관리하기 위한 저장소
일반적으로 Symbol()로 생성한 심볼은 고유하지만, 전역 심볼 레지스트리를 통해 특정 키를 가진 심볼을 공유할 수 있다.

Symbol vs Symbol.for

둘다 유일무이한 심벌 값을 생성하지만 Symbol 함수는 전역 심벌 레지스트리에 키를 지정할 수 없기 때문에 전역 심벌 레지스트리에서 등록돼 관리되지 않는다.

반면에 Symbol.for 메서드는 고유한 심벌이 전역 심벌 레지스트리에 등록되어 같은 심볼 값을 공유할 수 있다.

Symbol.keyFor(심벌 값)

전역 심벌 레지스트리에 등록된 심벌 값의 키를 추출 할 수 있다.

const mySymbol = Symbol.for("hello");
console.log(Symbol.keyFor(mySymbol)); // hello

const mySymbol2 = Symbol("hello");
console.log(mySymbol2.keyFor("hello")); // error

심벌과 상수

JS는 enum을 지원하지 않지만 Symbol을 활용하면 enum과 같은 역할을 하는 코드를 짤 수 있다.

enum

열거형이라 불리며, 미리 정의된 상수들의 집합을 나타내는 데이터 구조.
C,파이썬,JAVA등의 언어와 TS에서 지원한다.

Symbol을 enum의 값을 유일하게 보장하는 방식으로 활용할 수 있다.

TS의 enum

enum Direction {
  UP = "up",
  DOWN = "down",
  LEFT = "left",
  RIGHT = "right",
}

const myDirection: Direction = Direction.UP;

if (myDirection === Direction.UP) {
  console.log("you go to up");
}

Symbol을 사용하여 만든 열거형 집합

const Direction = Object.freeze({
  UP: Symbol("up"),
  DOWN: Symbol("down"),
  LEFT: Symbol("left"),
  RIGHT: Symbol("right"),
});

const myDirection = Direction.UP;

if (myDirection === Direction.UP) {
  console.log("you go to up");
}

Direction 객체를 변경 불가능하게만들었고 Symbol을 이용해 상수 값들을 고유한 값으로 만들었다.
이런식으로 enum과 같이 상수 값을 안전하게 관리할 수 있다.


프로퍼티 키

객체의 프로퍼티 키는 모든 문자열 또는 심벌 값으로 만들 수 있다.
심벌 값을 프로퍼티 키로 사용하는 방법은 심벌 값에 대괄호를 붙여주면 된다.
또한 프로퍼티에 접근할 때도 대괄호를 붙여줘야한다.

const obj = {
  [Symbol.for("mySymbol")]: 1,
};

console.log(obj[Symbol.for("mySymbol")]); // 1

심벌 값은 유일무이한 값이기 때문에 다른 프로퍼티 키와 절대 충돌하지 않는다.

프로퍼티 은닉

심벌 값을 프로퍼티 키로 사용해 생성한 프로퍼티는 for...in, Object.keys, Object.getOwnPropertyNames 메서드로 찾지 못한다.
즉 외부에 노출되지 않고 은닉된다.

왜냐하면 Symbol은 고유한 값을 가지기 때문에 기본적으로 열거되지 않는다.
(자바스크립트의 설계에 따른 특성)

const mySymbol = Symbol('mySymbol');
const obj = {
  [mySymbol]: 'This is a symbol value',
  regular: 'This is a regular value',
};

for (let key in obj) {
  console.log(key); // regular
}

그러나 Object.getOwnpropertySymbols 메서드를 사용하면 객체에서
심벌 프로퍼티만 배열로 반환할 수 있다.

console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol(mySymbol) ]


Well-known Symbol

자바스크립트에서 미리 정의한 특별한 심볼 값이 있다. 이를 빌트인 심벌이라고 한다.

빌트인 심벌 값들은 Symbol 함수의 프로퍼티에 기본적으로 할당되어 있다.

그리고 빌트인 심벌을 ECMAScrit 사양에서는 Well-known Symbol이라고 부르며
자바스크립트 엔진 내부 알고리즘에 사용된다.

대표적으로 Symbol.iterator가 있다.

Array,String,Map,Set,arguments,HTMLCollection,NodeList 같은 이터러블은 for...of문으로 순회 가능하다.

왜냐하면 이들은 Well-known Symbol인 Symbol.iterator를 키로 갖는 메서드를 가지기 때문이다.

Symbol.iterator 메서드를 호출하면 이터레이터를 반환하도록 ECMAScript 사양에 규정되어있다.
빌트인 이터러블은 이러한 규정, 이터레이션 프로토콜을 준수한다.

이 내용은 Iterator 파트에서 추가적으로 다루겠다.

핵심

즉, 이터레이션 프로토콜을 준수하기 위해 일반 객체에 존재해야하는 메서드의 키 Symbol.iterator고유한 값을 가져 다른 프로퍼티 키와 절대 중복되지 않는다는 것이다.
또한 Symbol.iterator를 키를 갖는 메서드가 이터레이션 프로토콜을 준수한다면
순회가 가능하다는 것이다.

profile
학습 기록.

0개의 댓글