1.심볼
통신 수학 쪽에서 심볼의 의미를 살펴보면 의미있는 하나의 정보단위 라고 한다. ES6(ECMAScript 2015)에서 새롭게 나온 symbol은 프로그램에서 유일무이한 유니크한 하나 밖에 없는 값이라고 보면 된다. 심볼은 루비와 같은 다른 프로그래밍언어에서도 사용하는 개념인 것 같다. (참조)
이런 유니크한 값은 주로 객체의 키로 사용된다. 오브젝트 안의 키라고 하면 유일무이한 값인데, 키를 중복해서 선언하면 덮어씌워진다.
const obj = { a: 1, b: 2, a: 3 };
console.log(obj.a); // 3
위의 코드에서 a라는 키가 두 번 정의되어 뒤에있는 a의 값이 출력되었다. 키가 중복되었다. 유일한 값이 아니게 되어버렸다.
이것 대신에 심볼을 키로 사용한다면 키가 중복되는 일을 막을 수 있다. 여기서 알 수 있듯이 우리가 쓰는 문자열을 키 값으로 지정해버리면 그 키 값은 유니크한 값이 될 수 없다. 그래서 Symbol이라는 유니크한 어떤 값을 나타내는 타입이 생겨난 것이다. 예를들어 이 세상에는 영희
라는 이름을 가진 사람이 여러명이다. 이 이름은 고유한 값을 나타낼 수 없으니 주민등록번호라는 심볼을 생성해서 사용한다고 보면 된다.
심볼을 어떻게 사용할까? 일단 심볼이 무엇인지 먼저 알아보자.
2.심볼은 새로나온 7번째 자바스크립트 타입
기존 자바스크립트의 타입에는 총 6개가 있었다. 원시타입(primitive type) 5개 (boolean, null, undefiend, number, string) 와 object type 이다.
어떤 값에다가 typeof 값
을 하면 그 타입이 나온다. 심볼을 만들고 심볼의 타입을 보면 심볼타입이라고 나온다.
const a = "문자";
console.log(typeof a); //string
const b = 123;
console.log(typeof b); //number
const c = null;
console.log(typeof c); //null
const d = undefined;
console.log(typeof d); //nudefiend
const e = false;
console.log(typeof e); //boolean
const f = {};
console.log(typeof f); //object
const g = Symbol();
console.log(typeof g); //symbol
3. 심볼 생성 방법
간단하다 Symbol Wrapper 함수를 실행하면 된다.
const sym = Symbol();
심볼을 생성할 때 구분하기 위한 데이터를 추가하여 생성할 수도 있다.
const sym = Symbol("value");
심볼타입은 신기하게도 같은 value를 인자로 주고 생성하더라도 값은 같지 얺다.
const sym1 = Symbol("value");
const sym2 = Symbol("value");
console.log(sym1 === sym2); // false
인자 값과 상관 없이 심볼은 고유하다.
4. 같은 심볼 사용 Symbol.for
같은 심볼을 사용하기 위해서는 Symbol.for를 사용한다.
// 전역 Symbol 레지스트리에 value라는 키로 저장된 Symbol이 없으면 새로운 Symbol 생성
const sym1 = Symbol.for("value");
// 전역 Symbol 레지스트리에 value라는 키로 저장된 Symbol이 있으면 해당 Symbol을 반환
const sym2 = Symbol.for("value");
console.log(sym1 === sym2); //true
5. 다른 원시타입과 다른점 New 못씀
심볼은 원시타입이라고 했는데 기존의 다른 원시타입과는 조금 다른 부분이 있다.
new 연산자를 통해 Wrapper 객체를 생성할 수 없다는 점 있다.
const str = new String("Hello");
const num = new Number(12);
const sym = new Symbol(); // TypeError!
number, string, boolean 등 기존의 원시타입은 new 연산자를 사용하여 생성할 수 있었지만 Symbol은 불가능하다.
본론부터 말하자면, 심볼 타입은 주로 고유한 객체의 프로퍼티의 값으로 사용하는 목적으로 쓰인다.
아래와 같이 객체의 프로퍼티에 접근하는 용도로 사용이 가능하다.
const a = Symbol();
const obj = {
a: "a 입니다",
[a]: "조금 다른 a 입니다",
};
obj.a;
obj[a];
6. 심볼 사용 예시
심볼을 어디에 쓸까 한 번 생각해봅시다. 유일무이한 값은 언제 필요할까?
Number.isNumber = arg => {
return typeof arg === "number" ? "숫자!" : "숫자 아님!";
};
const num = 10;
console.log(Number.isNumber(num)); // 숫자!
위와 같이 Number라는 객체에 isNumber
라는 이름을 가진 프로퍼티를 만들었다. 이 프로퍼티는 함수인데 인자로 넘어온 값의 타입이 number
인지 확인 하는 함수다. 이렇게 뿌듯하게 사용을 잘하고 있는데 갑자기 ECMA 표준으로 isNumber라는 똑같은 이름의 프로퍼티가 지정되었다면 어떻게 할까? 우리가 지정해 놓은 이 함수명과 중복되는 프로퍼티를 다른 곳에서 또 정의하였다면 이 객체를 사용하는 사용자 입장에서는 혼란스러워 진다. 위에서 언급했듯이 이름은 유일한 값이 아니기 때문이다.
하지만 표준으로 채택된 이후에는 위의 코드는 모두 사용할 수 없게 된다.
그렇다면 이 코드를 심볼을 이용해서 짜보자
const isNumber = Symbol();
Number[isNumber] = arg => {
return typeof arg === "number" ? "숫자!" : "숫자 아님!";
};
const num = 10;
if (Number[isNumber](num) === "숫자!") {
return num * num; // num 이 숫자일 때 실행할 로직..
}
이와같이 심볼을 객체의 프로퍼티로 지정하면 고유한 값으로 프로퍼티가 지정되어있기 때문에 나중에 isNumber가 표준으로 채택된다고 해도 사용자들은 그대로 Symbol을 통해 프로퍼티에 접근하기 때문에 문제가 없다. 심볼 타입을 통해 고유한 프로퍼티를 정의하고 접근할 수 있게 되었다.
7.Symbol.iterator
4.1 Symbol.iterator
Well-Known Symbol은 자바스크립트 엔진에 상수로 존재하며 자바스크립트 엔진은 Well-Known Symbol을 참조하여 일정한 처리를 한다. 예를 들어 어떤 객체가 Symbol.iterator를 프로퍼티 key로 사용한 메소드 가지고 있으면 자바스크립트 엔진은 이 객체가 이터레이션 프로토콜을 따르는 것으로 간주하고 이터레이터로 동작하도록 한다.
Symbol.iterator를 프로퍼티 key로 사용하여 메소드를 구현하고 있는 빌트인 객체(빌트인 이터러블)는 아래와 같다. 아래의 객체들은 이터레이션 프로토콜을 준수하고 있으며 이터러이터를 반환한다.
Array.prototype[Symbol.iterator]
String.prototype[Symbol.iterator]
Map.prototype[Symbol.iterator]
Set.prototype[Symbol.iterator]
NodeList.prototype[Symbol.iterator] HTMLCollection.prototype[Symbol.iterator]
arguments[Symbol.iterator]
// 이터러블
// Symbol.iterator를 프로퍼티 key로 사용한 메소드를 구현하여야 한다.
// 배열에는 Array.prototype[Symbol.iterator] 메소드가 구현되어 있다.
const iterable = ["a", "b", "c"];
// 이터레이터
// 이터러블의 Symbol.iterator를 프로퍼티 key로 사용한 메소드는 이터레이터를 반환한다.
const iterator = iterable[Symbol.iterator]();
// 이터레이터는 순회 가능한 자료 구조인 이터러블의 요소를 탐색하기 위한 포인터로서 value, done 프로퍼티를 갖는 객체를 반환하는 next() 함수를 메소드로 갖는 객체이다. 이터레이터의 next() 메소드를 통해 이터러블 객체를 순회할 수 있다.
console.log(iterator.next()); // { value: 'a', done: false }
console.log(iterator.next()); // { value: 'b', done: false }
console.log(iterator.next()); // { value: 'c', done: false }
console.log(iterator.next()); // { value: undefined, done: true }
정리
고유한 값을 갖고싶을 때는 문자열 대신 심볼을 사용하자