JS/[web] 코어 JS 튜토리얼

[코어 JS] 4.7 - 심볼형

web_seul 2021. 10. 11. 12:51
반응형

4.7 심볼형

JS는 객체 프로퍼티 키로 숫자형, 불린형은 불가능하고 오직 문자형과 심볼형만 허용

 

심볼(symbol)

심볼은 유일한 식별자를 만들고 싶을 때 사용, Symbol()을 사용하여 심볼값 생성

// id는 새로운 심볼이 됩니다.
let id = Symbol();

 

심볼을 만들 때 심볼 이름이라 불리는 설명을 붙일 수 있음, 디버깅시 유용

// 심볼 id에는 "id"라는 설명이 붙습니다.
let id = Symbol("id");

 

심볼은 유일성이 보장되는 자료형이므로 설명이 동일한 심볼 여러개를 만들어도 각 심볼값은 다름, 심볼에 붙은 설명(심볼 이름)은 어떤 것에도 영향을 주지않는 이름표 역할

let id1 = Symbol("id");
let id2 = Symbol("id");

alert(id1 == id2); // false

 

! 심볼은 문자형으로 자동 형 변환되지 않음

JS에선 문자형으로 암시적 형 변환이 비교적 자유롭게 발생하는 편이라 alert함수가 거의 모든 값을 인자로 받을 수 있는 이유, 그러나 심볼형 값은 다른 자료형으로 함시적 형 변환이 되지않음

let id = Symbol("id");
alert(id); // TypeError: Cannot convert a Symbol value to a string

 

문자열과 심볼은 근본이 다르므로 우연히라도 서로의 타입으로 변환되어서는 안됨, JS에선 '언어 차원의 보호장치'를 마련해 심볼형이 다른형으로 변환되지않게 막아줌

심볼을 반드시 출력해야하는 경우에는 .toString()메서드를 명시적으로 호출해서 사용

let id = Symbol("id");
alert(id.toString()); // Symbol(id)가 얼럿 창에 출력됨

symbol.description프로퍼티를 이용해서 설명만 보여주는 것도 가능

let id = Symbol("id");
alert(id.description); // id

 

- '숨김' 프로퍼티

심볼을 이용하여 '숨김(hidden)' 프로퍼티 생성 가능, 숨김 프로퍼티는 외부코드에서 접근 불가, 값 덮어쓰기가 불가한 프로퍼티

//서드파티 코드 : 다른 회사 제품에 이용되는 소프트웨어나 하드웨어를 개발하는 회사
let user = { // 서드파티 코드에서 가져온 객체
  name: "John"
};

let id = Symbol("id");
user[id] = 1;
alert( user[id] ); // 심볼을 키로 사용해 데이터에 접근할 수 있습니다.

//문자열 "id"가 아닌 Symbol("id")를 사용한 이유
//user는 서드파티 코드에서 가져온 객체로 함부로 새로운 객체 추가x
//심볼을 사용하여 서드파티 코드가 모르게 user에 식별자 부여 가능


//+ user를 식별해야하는 상황
//user의 원천인 서드파티코드, 현재 작성중인 스크립트, 제 3의 스크립트가
//각자 서로의 코드도 모른채 user를 식별해야하는 상황

//제 3의스크립트에서 Symbol("id")를 이용해 전용 식별자 생성
let id = Symbol("id");
user[id] = "제3 스크립트 id 값";

//심볼은 유일성이 보장되므로 충돌x
//but, 심볼대신 문자열 "id"를 사용해 식별자를 만들었다면 충돌발생 가능

let user = { name: "John" };

// 문자열 "id"를 사용해 식별자를 만들었습니다.
user.id = "스크립트 id 값";

// 만약 제3의 스크립트가 우리 스크립트와 동일하게 문자열 "id"를 이용해 식별자를 만들었다면...

user.id = "제3 스크립트 id 값"
// 의도치 않게 값이 덮어 쓰여서 우리가 만든 식별자는 무의미해집니다.

 

- Symbols in a literal

객체 리터럴{...}을 사용해 객체를 생성한 경우, 대괄호를 사용해 심볼형 키를 만들어야함

let id = Symbol("id");

let user = {
  name: "John",
  [id]: 123 // "id": 123은 안됨 -> 문자열"id"가 키가 됨
};

 

- 심볼은 for..in에서 배제됨

let id = Symbol("id");
let user = {
  name: "John",
  age: 30,
  [id]: 123
};

for (let key in user) alert(key); // name과 age만 출력되고, 심볼123은 출력되지 않습니다.

// 심볼로 직접 접근하면 잘 작동합니다.
alert( "직접 접근한 값: " + user[id] );

 

Object.key(user)에서도 키가 심볼인 프로퍼티는 배제됨

'심볼형 프로퍼티 숨기기(hiding symbolic property)'라 불리는 원칙 덕분에 외부 스크립트나 라이브러리는 심볼형 키를 가진 프로프티에 접근하지 못함, but Object.assign은 키가 심볼인 프로퍼티를 배제하지않곡 객체 내 모든 프로퍼티를 복사함

let id = Symbol("id");
let user = {
  [id]: 123
};

let clone = Object.assign({}, user);

alert( clone[id] ); // 123

객체를 복사하거나 병합할 때, 대가 id같은 심볼을 포함한 프로퍼티를 전부 사용하고 싶어할 것이라는 가정에 설계됨

 

- 전역 심볼(global symbol registry)

심볼은 이름이 같더라도 모두 별개로 취급되지만 같은 객체를 가리키길 원하는 경우를 위해 만들어진 전역 심볼 레지스트리, 전역 심볼 레지스트리 안에 심볼을 만들고 해당 심볼에 접근하면 이름이 같은 경우 항상 동일한 심볼을 반환, 애플리케이션에서 광범위하게 사용해야하는 심볼인 경우 전역 심볼 사용

레지스트리 안에 있는 심볼을 읽거나 새로운 심볼을 생성하려면 Symbol.for(key)를 사용

해당 메서드를 호출하면 이름이 key인 심볼 바놘, 조건에 맞는 심볼이 레지스트리 안에 없는 경우 새로운 심볼 Symbol(key)를 만들고 레지스트리 안에 저장

// 전역 레지스트리에서 심볼을 읽습니다.
let id = Symbol.for("id"); // 심볼이 존재하지 않으면 새로운 심볼을 만듭니다.

// 동일한 이름을 이용해 심볼을 다시 읽습니다(좀 더 멀리 떨어진 코드에서도 가능합니다).
let idAgain = Symbol.for("id");

// 두 심볼은 같습니다.
alert( id === idAgain ); // true

 

- Symbol.kefFor

이름을 얻을 수 있는 메서드, Symbol.for(key)와 반대

// 이름을 이용해 심볼을 찾음
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");

// 심볼을 이용해 이름을 얻음
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id

Symbol.keyFor는 전역 심볼 레지스트리를 뒤져서 해당 심볼의 이름을 얻어냄, 검색 범위가 전역 심볼 레지스트리이기 때문에 전역 심볼이 아닌 심볼에는 사용불가, 전역 심볼이 아닌 인자가 넘어오면 Symbol.keyFor는 undefined반환

전역심볼이 아닌 모든 심볼은 description 프로퍼티가 있으므로 일반 심볼에서는 description 프로퍼티 사용

let globalSymbol = Symbol.for("name");
let localSymbol = Symbol("name");

alert( Symbol.keyFor(globalSymbol) ); // name, 전역 심볼
alert( Symbol.keyFor(localSymbol) ); // undefined, 전역 심볼이 아님

alert( localSymbol.description ); // name

 

- 시스템 심볼(system symbol)

JS내부에서 사용되는 심볼, 객체를 미세 조정 가능

잘 알려진 심볼(well-known symbols)

- Symbol.hasInstance

- Symbol.isConcatSpreadable

- Symbol.iterator

- Symbol.toPrimitive

객체가 원시형으로 변환되는 방식 Symbol.toPrimitive

 

요약

- Symbol은 원시형데이터로 유일무이한 식별자 생성시 사용

- Symbol()을 호출하면 심볼 생성 가능, 설명(이름)은 선택적 추가 가능

- 심볼은 이름이 같아도 값은 항상 다름, 값도 같길 원할 경우에는 전역 레지스트리 사용

  Symbol.for(key)는 key라는 이름의 전역 심볼 반환, key라는 전역 심볼이 없을 경우 새로운 전역 심볼 생성, key가 같다면 Symbol.for는 어디서 호출하든 같은 심볼 반환

 

심볼의 주요 유스 케이스

1. 객체의 '숨김' 프로퍼티 - 외부 스크립트, 라이브러리에 '속한' 객체에 새로운 프로퍼티 추가할 경우 심볼을 만들고 이를 프로퍼티 키로 사용, 키가 심볼인 경우엔 for..in의 대상이 되지않아서 의도치 않게 프로퍼티가 수정되는것 예방, 외부 스크립트나 라이브러리는 심볼 정보를 갖고있지 않으므로 프로펕티에 직접 접근 불가능, 심볼형 키를 사용하면 프로ㅓ티가 우연히사용되거나 덮어씌어지는 경우를 예방이 가능함

2. JS 내부에서 사용되는 시스템 심볼은 Symbol.*로 접근 가능, 시스템 심볼을 이용하면 내장 메서드 등의 기본 동작을 입맛대로 변경 가능 (iterable 객체에선 Symbol.iterator를, 객체를 원시형으로 변환하기엔 Symbol.toPrimitive이 어떻게 사용되는지)

내장 메서드 Object.getOwnPropertySymbols(obj)를 사용하면 모든 심볼을 볼 수 있고 메서드 Reflect.ownkeys(obj)는 심볼형 키를 포함한 객체의 모든키 반환 가능

 

반응형