티스토리 뷰

Javascript

ES6의 Symbol

심재철 2020. 7. 20. 14:02

ES6에서 number, boolean, string, undefined, null, object 이외에 7번째 새로운 자료 구조인 Symbol이 등장했다.

Symbol

심볼은 unique id를 생성해주는 factory 함수라고 생각하면 편하다.

let symbol2 = Symbol('simsimjae'); 

이런식으로 simsimjae라고 하는 이름을 가진 심볼을 생성할 수 있다. 여기서 'simsimjae'의 역할은 심볼에 이름을 붙인거(description)라고 생각하면 된다. 따라서 같은 이름을 가진 심볼을 2개를 만들었다고 해서 그 두개의 심볼이 같은것은 아니다.

Symbol('simsimjae') !== Symbol('simsimjae')

왜냐면 심볼은 생성할때마다 유니크한 값이 되기 때문이다.

 

Symbol의 사용처

객체의 property key로 사용된다.

const SymbolPropertyKey = Symbol();
let obj = {};

obj[SymbolPropertyKey] = 123;
console.log(obj[SymbolPropertyKey]); // 123

좀 더 간단하게는 이렇게 사용할 수 있다.

const SymbolPropertyKey = Symbol();
let obj = {
    [SymbolPropertyKey]: 123
};

좀 더 간단하게는 이렇게 사용할 수 있다.

let obj = {
    [Symbol('SymbolPropertyKey')]: 123
};

이런식으로 객체의 메소드를 가리킬수도 있다.

const FOO = Symbol();
let obj = {
    [FOO]() {
        return 'bar';
    }
};
console.log(obj[FOO]()); // bar

FOO라는 심볼(unique한 string값이라고 생각하자.) 메소드를 선언했다.

 

객체의 키로 string을 사용하게 되면 이런 문제가 발생할 수 있다.

const obj = {
    'hello': 'world',
    'hello': 'here'
};

나중에 나온 hello key가 그 위에서 선언된 key를 덮어 씌워서

console.log(obj.hello) // "here"를 출력함

이렇게 된다.

그래서 객체의 키로 심볼을 사용하게 될 경우 description이 똑같다 하더라도 매번 고유한 값이 생성되기 때문에 property key가 충돌되지 않을거라고 확신할 수 있다. 심볼은 이런 이름 충돌에 대한 니즈 때문에 탄생하게 되었다.

 

own property key를 열거하는 방법

ES5까지는 객체의 키로 무조건 string만 사용할 수 있었지만 ES6에서는 심볼을 객체의 키로 사용할 수 있도록 추가되었다. 애초에 심볼의 탄생 목적이 객체의 property key를 unique하게 만들기 위한 목적이라고 한다.

let obj = {
    [Symbol('my_key')]: 1,
    enum: 2,
    nonEnum: 3
};

symbol은 열거가능하지 않다. 여기서 열거의 의미는 for .. in 문이나 object spread에서 확인이 가능한 값이냐는 소리다.

따라서, 만약 아래와 같이

Object.keys(obj)
['enum', 'nonEnum'] 출력

Object.keys를 사용해서 객체의 모든 키를 배열로 만들어도 Symbol은 노출이 되지 않는다. Symbol은 enumerable(열거가능)하지 않기 때문이다.

 

만약 obj의 nonEnum property도 non enumerable로 만들고 싶으면 아래와 같이 하면 된다.

Object.defineProperty(obj,
    'nonEnum', { enumerable: false });

 

한번 더 Object.keys를 호출해보면 

Object.keys(obj)
['enum'] 출력

enum만 출력된다.

객체 내부의 property들을 확인하는 3가지 방법

ES6에서는 객체의 키로 string or Symbol 두가지가 가능하다고 했다. 따라서, 키가 string이냐 Symbol이냐에 따라서 호출해야 하는 함수가 달라진다.

Object.getOwnPropertyNames(obj)

이 함수를 호출하면 객체의 key가 string인것들만 출력해준다. (enum, nonEnum출력)

Object.getOwnPropertySymbols(obj)

이 함수를 호출하면 객체의 key가 Symbol인것들만 출력해준다. ( [Symbol(my_key)] 출력)

 

만약 key가 Symbol이냐 string이냐에 상관없이 모든 property를 출력하고 싶을땐

Reflect.ownKeys(obj)
[Symbol(my_key), 'enum', 'nonEnum']

이렇게 해주면 된다. Reflect란 (거울) '비추다' 라는 뜻이다. 한마디로 객체 내부를 비춰 뭐가 있는지 확인한다고 생각하면 편하다.

 

Symbol의 scope

심볼은 우리가 흔히 global scope라고 부르는 것(브라우저에서는 window객체)보다 더 global하다. 아이프레임을 사용해본적이 있는 개발자들은 아이프레임의 scope와 현재 scope가 다르다는것을 알것이다.

function test(arr) {
  var iframe = frames[0];
  
  // 아이프레임의 Scope와 현재 javascript의 global Scope는 다르다.
  console.log(Array === iframe.Array); // false
  console.log(arr instanceof Array); // false, arr은 iframe에서 전달한 배열
  console.log(arr instanceof iframe.Array); // true

  // 그러나 심볼은 아이프레임과도 scope를 공유한다.
  console.log(Symbol.iterator === iframe.Symbol.iterator); // true
}

그래서 우리가 기존에 알고 있던 global은 사실 진짜 global이 아니다. 현재 자바스크립트가 동작하고 있는 호스트 환경(브라우저, 노드)에 따라 global은 얼마든지 변경될 수 있다. 진짜 global한 값을 만들고 싶으면 심볼을 사용해야 한다.

global symbol의 위치

자바스크립트 엔진에는 Symbol registry라고 하는것이 존재한다.

string : Symbolentry로 이루어진 map이라고 생각하면 되는데 엔진에 존재하기 때문에 진짜 global한 값을 만들 수 있다.

 

이곳에 어떤 심볼을 집어 넣어 두면 모든 영역에서 유일한 값을 저장해놓고 쓸 수 있다.

그렇게 하기 위해서는 아래 메소드를 사용 해야 한다.

let sym = Symbol.for('Hello everybody!'); // Symbol registry에 유니크한 공유 심볼 생성

이 Symbol.for 메소드는 symbol registry에서 Hello everybody!라는 string이 있는지 없는지 찾아본다음에 있으면 그걸 리턴해주고 없으면 새로 만들어주는 메소드이다.

 

그냥 한마디로 global registry를 찾아보는 메소드라고 생각하자.

Symbol.keyFor(sym)

 

 

 

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함