33장 7번째 데이터 타입 Symbol

이로그·2024년 2월 13일
0

33장 7번째 데이터 타입 Symbol

33.1 심벌이란?

  • 심벌 : ES6에서 도입된 7번째 데이터 타입으로 변경 불가능한 원시 타입의 값이다.
  • 주로 이름의 충돌 위험이 없는 유일한 프로퍼티 키를 만들기 위해 사용한다.

33.2 심벌 값의 생성

33.2.1 Symbol 함수

  • 심벌 값은 Symbol 함수를 호출하여 생성한다.
  • 다른 데이터 타입과 다르게 리터럴 표기법으로 생성할 수 없다.
  • 이때 생성된 심벌 값은 외부로 노출되지 않아 확인할 수 없으며, 다른 값과 절대 중복되지 않는 유일무이한 값이다.
  • new 연산자와 함께 호출하지 않는다.
  • 선택적으로 문자열을 인수로 전달할 수 있는데, 이 문자열은 생성된 심벌 값에 대한 설명으로 디버깅용도로만 사용된다.
const mySymbol = Symbol();
console.log(typeof mySymbol); // symbol

new Symbol(); // TypeError

// 심벌 값에 대한 설명이 같더라도 유일무이한 심벌 값을 생성한다.
const mySymbol1 = Symbol('mySymbol');
const mySymbol2 = Symbol('mySymbol');

console.log(mySymbol1 === mySymbol2); // false
  • 심벌 값도 객체처럼 접근하면 암묵적으로 래퍼 객체를 생성한다.
const mySymbol = Symbol('mySymbol');

console.log(mySymbol.description); // mySymbol
console.log(mySymbol.toString()); // Symbol(mySymbol)
  • 심벌 값은 암묵적으로 문자열이나 숫자 타입으로 변환되지 않는다.
  • 단, 불리언 타입으로는 암묵적으로 타입 변환된다.
const mySymbol = Symbol('mySymbol');

consol.log(mySymbol + ''); // TypeError
consol.log(+mySymbol); // TypeError

console.log(!!mySymbol); // true
if(mySymbol) console.log('mySymbol is not empty.');

32.2.2 Symbol.for / Symbol.keyFor 메서드

  • Symbol.for 메서드는 인수로 전달받은 문자열을 키로 사용하여 키와 심벌 값으 쌍들이 저장되어 있는 전역 심벌 레지스트리에서 해당 키와 일치하는 심벌 값을 검색한다.
    • 검색 성공시, 검색된 심벌 값을 반환한다.
    • 검색 실패시, 새로운 심벌 값을 생성하여 Symbol.for 메서드의 인수로 전달된 키로 전역 심벌 레지스트리에 저장한 후, 생성된 심벌 값을 반환한다.
// 전역 심벌 레지스트리에 mySymbol이라는 키로 저장된 심벌 값이 '없으면' 새로운 심벌 값을 생성
const s1 = Symbol.for('mySymbol');
// 전역 심벌 레지스트리에 mySymbol이라는 키로 저장된 심벌 값이 '있으면' 해당 심벌 값을 반환
const s2 = Symbol.for('mySymbol');

console.log(s1 === s2); // true
  • Symbol함수는 호출될 때마다 유일무이한 심벌 값을 생성하지만, 심벌 값을 검색할 수 있는 키를 지정할 수 없다.
  • 하지만, Symbol.for 메서드를 사용하면 유일무이한 상수인 심벌 값을 단 하나만 생성하여 전역 심벌 레지스트리를 통해 공유할 수 있다.
  • Symbol.keyFor 메서드를 사용하면 전역 심벌 레지스트리에 저장된 심벌 값의 키를 추출할 수 있다.
const s1 = Symbol.for('mySymbol');
Symbol.keyFor(s1); // mySymbol

const s2 = Symbol('foo');
Symbol.keyFor(s2); // undefined

33.3 심벌과 상수

  • 예를 들어, 값에는 특별한 의미가 없고, 상수 이름 자체에 의미가 있는 경우가 있다. 이때 문제는 상수 값이 변경될 수도 있고, 다른 변수 값과 중복될 수도 있다.
  • 이러한 경우 중복될 가능성이 없는 유일무이한 심벌 값을 사용할 수 있다.
// as is
const Direction = {
    UP: 1,
    DOWN: 2,
    LEFT: 3,
    RIGHT: 4
};

const myDirection = Direction.UP;

if(myDirection === Direction.UP) {
    console.log('You are going Up.');
}

// to be
const Direction = {
    UP: Symbol('up'),
    DOWN: Symbol('down'),
    LEFT: Symbol('left'),
    RIGHT: Symbol('right')
};

const myDirection = Direction.UP;

if(myDirection === Direction.UP) {
    console.log('You are going Up.');
}

33.4 심벌과 프로퍼티 키

  • 객체의 프로퍼티 키는 심벌 값으로도 만들 수 있고, 동적으로 생성할 수도 있다.
  • 심벌 값을 프로퍼티 키로 사용하려면 프로퍼티 키로 사용할 심벌 값에 대괄호를 사용해야 한다. 프로퍼티에 접근할 때도 마찬가지로 대괄호를 사용해야 한다.
const obj = {
    [Symbol.for('mySymbol')]: 1
};

obj[Symbol.for('mySymbol')]; // 1
  • 심벌 값은 유일무이한 값이므로 심벌 값으로 프로퍼티 키를 만들면 다른 프로퍼티 키와 절대 충돌하지 않는다.

33.5 심벌과 프로퍼티 은닉

  • 심벌 값으로 프로퍼티 키를 사용하여 생성한 프로퍼티는 for ... in 문이나 Object.keys, Object.getOwnPropertyNames 메서드로 찾을수 없다.
  • 심벌 값을 프로퍼티 키로 사용하여 프로퍼티를 생성하면 외부에 노출할 필요가 없는 프로퍼티를 은닉할 수 있다.
  • 하지만 완벽하게 숨기는 것은 불가능하다. ES6에서 도입된 Object.getOwnPropertySymbols 메서드를 사용하면 심벌 값을 프로퍼티 키로 사용하여 생성한 프로퍼티 키를 찾을 수 있다.
const obj = {
    [Symbol.for('mySymbol')]: 1
};

for(const key in obj) {
    console.log(key); // 이무것도 출력되지 않는다.
}

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

const symbolKey1 = Object.getOwnPropertySymbols(obj)[0];
console.log(obj[symbolKey1]); // 1

33.6 심벌과 표준 빌트인 객체 확장

  • 일반적으로 표준 빌트인 객체에 사용자 정의 메서드를 직접 추가하여 확장하는 것은 권장하지 않는다. 개발자가 추가한 메서드와 미래에 표준사양으로 추가될 메서드의 이름이 중복 될 수 있기 때문이다.
  • 하지만 중복될 가능성이 없는 심벌 값으로 프로퍼티 키를 생성하여 표준 빌트인 객체를 확장하면 어떤 프로퍼티 키와도 충돌할 위험이 없어 안전하게 표준 빌트인 객체를 확장할 수 있다.
Array.prototype[Symbol.for('sum')] = function() {
    return this.reduce((acc, cur) => acc + cur, 0);
};

[1,2][Symbol.for('sum')](); // 3

33.7 Well-known Symbol

  • 자바스크립트가 기본 제공하는 빌트인 심벌 값이 있다.
  • 빌트인 심벌 값은 Symbol 함수의 프로퍼티에 할당되어 있고, ECMAScript 사양에서는 Well-known Symbol이라 부른다.
  • 예를 들어, for ... of문으로 순회 간으한 빌트인 이터러블은 Well-known Symbol인 Symbol.iterator를 키로 갖는 메서드를 가지며, Symbol.iterator 메서드를 호출하면 이터레이터를 반환하도록 ECMAScript 사양에 규정되어 있다.
  • 만약 빌트인 이터러블이 아닌 일반 객체를 이터러블처럼 동작하도록 구현하고 싶다면 이터레이션 프로토콜을 따르면 된다.
  • Symbol.iterator를 키로 갖는 메서드를 객체에 추가하고 이터레이터를 반환하도록 구현하면 그 객체는 이터러블이 된다.
// 1 ~ 5 번위의 정수로 이루어진 이터러블
const iterable = {
    // Symbol.iterator 메서드를 구현하여 이터러블 프로토콜을 준수
    [Symbol.iterator]() {
        let cur = 1;
        const max = 5;

        // Symbol.iterator 메서드는 next 메서드를 소유한 이터레이터를 반환
        return {
            next() {
                return { value: cur++, done: cur > max + 1 };
            }
        }
    }
}

for(const num of iterable) {
    console.log(num); // 1 2 3 4 5
}
  • 이터레이션 프로토콜을 준수하기 위해 일반 객체에 추가해야 하는 메서드의 키 Symbol.iterator는 기존 프로퍼티 키 또는 미래에 추가될 프로퍼티 키와 절대로 중복되지 않을 것이다.
  • 이처럼 심볼은 하위 호황성을 보장하기 위해 도입되었다.

0개의 댓글