js에서 몇 안되는 원시타입 중 하나. Symbol을 통해 생성되면 항상 유니크한 식별자가 보장된다(예외 경우도 있다)
Symbol("test") == Symbol("test"); //false
const obj = {};
const keyA1 = Symbol("A");
const keyA2 = Symbol("A");
obj[keyA1] = "A1";
obj[keyA2] = "A2";
console.log(obj);
/*
{
Symbole(A): "A1"
Symbole(A): "A2"
}
*/얼핏 보면 똑같은 프로퍼티 키값이 존재하는걸로 보이지만 당연히 프로퍼티 키값은 다르다. (keyA1 !== keyA2 이기 때문에) 심볼 생성 시 넘겨준 인자값은 단순 심볼 설명(구분)을 위한 값일 뿐, 그 이상의 역할을 하지는 않는다.
심볼 인자값을 description 용도가 아닌 진짜 키의 일부분으로 쓰기 위해 만들어진 심볼 객체 생성 메소드. Symbol 은 항상 새로운 객체를 만들어내지만 Symbol.for는 파라미터 값에 따라 동일한 심볼이 반환 될 수도 있다.
Symbol.for를 통해 생성 된 심볼은 먼저 해당 파라미터로 전역 심볼 레지스트리(global symbol registry) 내 동일한 값이 있는지 찾는다. 만약 없다면 새로운 심볼을 생성해서 만든 후, 전역 심볼 레지스트리에 등록한다. 그 후 Symbol.for을 통해 동일한 파라미터로 심볼을 생성하려고 하면 기존에 생성된 심볼을 반환 해준다.
const obj = {};
const keyA1 = Symbol.for("A");
const keyA2 = Symbol.for("A");
obj[keyA1] = "A1";
obj[keyA2] = "A2";
console.log(obj);
/*
{
Symbole(A): "A2"
}
*/위에서 설명 했듯이, Symbol.for("A") === Symbol.for("A") 는 true를 반환하므로 객체엔 오직 하나의 프로퍼티만 존재하게 된다.
다른 키와 겹치지 않는 유일한 프로퍼티 키값으로 사용하기 위해 js 내부적으로 이미 많은 심볼들이 정의되어 있다. 주요 프로퍼티를 실수로 덮어씌우면 의도치 않는 버그가 일어날 수도 있으므로 이런 프로퍼티는 외부에서 볼 수 없게 시스템 적으로 숨겨놓고 오직 미리 정의된 심볼을 통해서만 접근 할 수 있게끔 만들어놓으면, 개발자가 필요할때 정의 된 심볼을 통해 분명한 목적을 가지고 접근 한 것이라 판단 할 수 있으므로 버그를 줄여주는데 많은 역할을 하고 있다.
대표적으로 미리 정의 된 심볼들은 Symbol.match , Symbol.replace , Symbol.search, Symbol.iterator 등이 존재한다.
Iterable, 그니까 순환 가능한 객체를 만들기 위해선 java 에서는 Iterator 를 구현하면 되고, javascript에선 내부 프로퍼티인 @@iterator 를 구현해주면 된다. 근데 이건 일반적으로 접근이 불가능한데, 이 객체에 접근하기 위해 사용되는 심볼이 바로 Symbol.iterator 이다.
const arr = [1,2,3]
const it = arr[Symbol.iterator]()
it.next(); //{value:1,done:false}
it.next(); //{value:2,done:false}
it.next(); //{value:3,done:false}
it.next(); //{value: undefined, done:true}
it.next(); //{value: undefined, done:true}
it.next(); //{value: undefined, done:true}
...
...Array에 순환 객체(iterator)를 생성하기 위해 Symbol.iterator을 통해 접근하여 iterator를 생성하고 next를 통해 반복적으로 호출해 보면 array에 저장된 값들을 전부 순환해서 확인 할 수가 있다. (iterator에 대한 자세한 설명은 생략)
js에서 기본적으로 array, map, set 등은 @@iterator 가 구현되어 있어서 순환 가능한 객체로 분류된다. js에서 순환 가능한 객체라는 의미는 for...of나 spread를 사용 할 수 있다는 점이 큰 특징이다.
const arr = [1,2,3,4,5]
const obj = {
[Symbol.iterator] : function *(){
for(const val of arr){
yield val
}
}
}
//obj 는 실질적으로 빈 깡통인데 iterator만 구현 해줘서 외부 array값을 순환 할 수 있게 만들 수 있다.
console.log(..obj) //1 2 3 4 5. --> spread
for(const _val of obj){
console.log(_val) // 1,2,3,4,5 가 순서대로 출력된다.
}generator 문법은 이미 포스트 해놓은 것이 있으니 생략하고, 이런식으로 iterator만 구현해서 외부객체를 마치 내가 가지고 있는것처럼 프로퍼티를 만들어 놓을 수가 있다.
다시 말하지만 spread, for...of는 iterator(@@iterator)에 의존하게 된다.