[JS] ๐ ์๋ฐ์คํฌ๋ฆฝํธ ์๋ฃํ Symbol ๐ฉ ์ ๋ฆฌ
Symbol์ด๋?
1997๋ ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ECMAScript๋ก ์ฒ์ ํ์คํ๋ ์ด๋๋ก ์๋ฐ์คํฌ๋ฆฝํธ๋ 6๊ฐ์ ํ์ ์ ๊ฐ์ง๊ณ ์์์ต๋๋ค.
[์์ ํ์ (primitive data type)]
- Boolean
- null
- undefined
- Number
- String
[๊ฐ์ฒด ํ์ (Object type)]
- Object
์ฌ๋ณผ(symbol)์ ES6์์ ์๋กญ๊ฒ ์ถ๊ฐ๋ 7๋ฒ์งธ ํ์ ์ผ๋ก ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ ์์ ํ์ ์ ๊ฐ์ ๋๋ค.
์ฌ๋ณผ์ ์ฃผ๋ก ์ด๋ฆ์ ์ถฉ๋ ์ํ์ด ์๋ ์ ์ผํ ๊ฐ์ฒด์ ํ๋กํผํฐ ํค(property key)๋ฅผ ๋ง๋ค๊ธฐ ์ํด ์ฌ์ฉํฉ๋๋ค.
Symbol์ ์์ฑ
์ฌ๋ณผ์ 3๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ์์ฑํ ์ ์์ต๋๋ค.
Symbol()Symbol.for()Symbol.iterator
โ
Symbol()
Symbol์ Symbol() ํจ์๋ก ์์ฑํฉ๋๋ค.
์ด๋ ์์ฑ๋ Symbol์ ๊ฐ์ฒด๊ฐ ์๋๋ผ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ ์์ ํ์ ์ ๊ฐ์ ๋๋ค.
Symbol() ํจ์๋ String, Number, Boolean๊ณผ ๊ฐ์ด ๋ํผ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ์์ฑ์ ํจ์์๋ ๋ฌ๋ฆฌ new ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ์ง ์์ต๋๋ค.
let mySymbol = Symbol();
let mySymbol2 = Symbol("something");
// Symbol()์ ๋ฌธ์์ด์ ์ค์ ์๋๋ฐ ๋ณ๋ค๋ฆ ๋ป์ ์๊ณ ์ฃผ์ ๊ฐ์ ๊ฐ๋
์ด๋ค.
// ๋๋ฒ๊น
ํ ๋ ์ด ์ฌ๋ณผ์ด ์ด๋ ์ฌ๋ณผ์ธ์ง ๊ตฌ๋ถํ๊ธฐ ํธํ๊ฒ ํ๊ธฐ ์ํ ์ฅ์น
console.log(mySymbol); // Symbol()
console.log(typeof mySymbol); // symbol
โ์ฌ๋ณผ์ ์ ์ผ์ฑ์ด ๋ณด์ฅ๋๋ ์๋ฃํ์ด๊ธฐ ๋๋ฌธ์, ์ค๋ช ์ด ๋์ผํ ์ฌ๋ณผ์ ์ฌ๋ฌ ๊ฐ ๋ง๋ค์ด๋ ๊ฐ ์ฌ๋ณผ๊ฐ์ ๋ค๋ฆ ๋๋ค.
์ฌ๋ณผ์ ๋ถ์ด๋ ์ค๋ช (์ฌ๋ณผ ์ด๋ฆ)์ ์ด๋ค ๊ฒ์๋ ์ํฅ์ ์ฃผ์ง ์๋ ์ด๋ฆํ ์ญํ ๋ง์ ํฉ๋๋ค.
let id1 = Symbol("id");
let id2 = Symbol("id");
alert(id1 == id2); // false
โ
Symbol.for()
- ์ ์ญ ์ฌ๋ณผ
Symbol() ์ ๊ณ ์ ํ ์ฌ๋ณผ์ ๋ฐํํฉ๋๋ค.
ํ์ง๋ง Symbol.for() ๋ ์ ์ญ์ผ๋ก ์กด์ฌํ๋ global symbol table ์ ๋ชฉ๋ก์ ์ฐธ์กฐํฉ๋๋ค.
๋๋ฌธ์ Symbol.for(token string) ์ผ๋ก ์ ์ํ ๋, token string ์ผ๋ก ์ ์ ๋ ์ฌ๋ณผ์ด ์๋ค๋ฉด, ํด๋น ์ฌ๋ณผ์ ๋ฐํํ๊ฒ ๋ฉ๋๋ค.
// ์ ์ญ Symbol ๋ ์ง์คํธ๋ฆฌ์ foo๋ผ๋ ํค๋ก ์ ์ฅ๋ Symbol์ด ์์ผ๋ฉด ์๋ก์ด Symbol ์์ฑ
const s1 = Symbol.for('foo');
// ์ ์ญ Symbol ๋ ์ง์คํธ๋ฆฌ์ foo๋ผ๋ ํค๋ก ์ ์ฅ๋ Symbol์ด ์์ผ๋ฉด ํด๋น Symbol์ ๋ฐํ
const s2 = Symbol.for('foo');
console.log(s1 === s2); // true
โ
Symbol.keyFor()
Symbol.keyFor ์ global symbol table ๋ก๋ถํฐ ์กด์ฌํ๋ Symbol์ token string ์ ๋ฐํํฉ๋๋ค.
var token = Symbol.for("tokenString");
console.log(Symbol.keyFor(token) === "tokenString"); // true
โ
Symbol.description
Symbol.keyFor๊ฐ ์ ์ญ ์ฌ๋ณผ์ ์ด๋ฆ์ ๋ฐํํ๋ฉด,
์ด๊ฑด ์ผ๋ฐ ์ฌ๋ณผ ์ด๋ฆ์ ๋ฐํํฉ๋๋ค.
const shareSymbol = Symbol.for('myKey');
console.log(Symbol.keyFor(shareSymbol)); // myKey
const unsharedSymbol = Symbol('myKey');
console.log(Symbol.keyFor(unsharedSymbol)); // undefined
console.log(unsharedSymbol.description); // myKey
โSymbol ํจ์๋ ๋งค๋ฒ ๋ค๋ฅธ Symbol ๊ฐ์ ์์ฑํ๋ ๊ฒ์ ๋ฐํด,
Symbol.for ๋ฉ์๋๋ ํ๋์ Symbol์ ์์ฑํ์ฌ ์ฌ๋ฌ ๋ชจ๋์ด ํค๋ฅผ ํตํด ๊ฐ์ Symbol์ ๊ณต์ ํ ์ ์๋ค.
Symbol.for ๋ฉ์๋๋ฅผ ํตํด ์์ฑ๋ Symbol ๊ฐ์ ๋ฐ๋์ ํค๋ฅผ ๊ฐ๋๋ค.
์ด์ ๋ฐํด Symbol ํจ์๋ฅผ ํตํด ์์ฑ๋ Symbol ๊ฐ์ ํค๊ฐ ์๋ค.
Symbol์ ์ฌ์ฉ
โ๊ฐ์ฒด์ ํ๋กํผํฐ ํค๋ ๋น ๋ฌธ์์ด์ ํฌํจํ๋ ๋ชจ๋ ๋ฌธ์์ด๋ก ๋ง๋ค ์ ์์ต๋๋ค.
const obj = {};
const v = "name";
obj[v] = 'myProp';
obj[123] = 123; // 123์ ๋ฌธ์์ด๋ก ๋ณํ๋๋ค.
// obj.123 = 123; // SyntaxError: Unexpected number
obj['prop' + 123] = false;
console.log(obj);
/*
{
name : 'myProp',
'123' : 123,
prop123 : false
}
*/
Symbol ๊ฐ๋ ๊ฐ์ฒด์ ํ๋กํผํฐ ํค๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
Symbol ๊ฐ์ ์ ์ผํ ๊ฐ์ด๋ฏ๋ก Symbol ๊ฐ์ ํค๋ก ๊ฐ๋ ํ๋กํผํฐ๋ ๋ค๋ฅธ ์ด๋ ํ ํ๋กํผํฐ์๋ ์ถฉ๋ํ์ง ์์ต๋๋ค.
const obj = {};
const mySymbol = Symbol('mySymbol');
const mySymbol2 = Symbol('mySymbol');
obj[mySymbol] = 123;
obj[mySymbol2] = 456;
console.log(obj); // { [Symbol(mySymbol)] : 123, Symbol(mySymbol)] : 456}
console.log(obj[mySymbol]); // 123
โโ
๐ก ์ฌ๋ณผ์ ๋ฌธ์ํ์ผ๋ก ์๋ ํ ๋ณํ๋์ง ์์ต๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ์์ ๋ฌธ์ํ์ผ๋ก์ ์์์ ํ ๋ณํ์ด ๋น๊ต์ ์์ ๋กญ๊ฒ ์ผ์ด๋๋ ํธ์ ๋๋ค.
alert ํจ์๊ฐ ๊ฑฐ์ ๋ชจ๋ ๊ฐ์ ์ธ์๋ก ๋ฐ์ ์ ์๋ ์ด์ ๊ฐ ์ด ๋๋ฌธ์ด์ฃ .
โ๊ทธ๋ฌ๋ ์ฌ๋ณผ์ ์์ธ์ ๋๋ค.
์ฌ๋ณผํ ๊ฐ์ ๋ค๋ฅธ ์๋ฃํ์ผ๋ก ์์์ ํ ๋ณํ(์๋ ํ ๋ณํ)๋์ง ์์ต๋๋ค.
์๋ ์์์์ alert๋ ์๋ฌ๋ฅผ ๋ฐ์์ํต๋๋ค.
let id = Symbol("id");
alert(id); // TypeError: Cannot convert a Symbol value to a string
๋ฌธ์์ด๊ณผ ์ฌ๋ณผ์ ๊ทผ๋ณธ์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ์ฐ์ฐํ๋ผ๋ ์๋ก์ ํ์ ์ผ๋ก ๋ณํ๋ผ์ ์ ๋ฉ๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ์์ '์ธ์ด ์ฐจ์์ ๋ณดํธ์ฅ์น(language guard)'๋ฅผ ๋ง๋ จํด ์ฌ๋ณผํ์ด ๋ค๋ฅธ ํ์ผ๋ก ๋ณํ๋์ง ์๊ฒ ๋ง์์ค๋๋ค.
์ฌ๋ณผ์ ๋ฐ๋์ ์ถ๋ ฅํด์ค์ผ ํ๋ ์ํฉ์ด๋ผ๋ฉด ์๋์ ๊ฐ์ด .toString() ๋ฉ์๋๋ฅผ ๋ช ์์ ์ผ๋ก ํธ์ถํด์ฃผ๋ฉด ๋ฉ๋๋ค.
let id = Symbol("id");
alert(id.toString()); // Symbol(id)๊ฐ ์ผ๋ฟ ์ฐฝ์ ์ถ๋ ฅ๋จ
symbol.description ํ๋กํผํฐ๋ฅผ ์ด์ฉํ๋ฉด ์ค๋ช ๋ง ๋ณด์ฌ์ฃผ๋ ๊ฒ๋ ๊ฐ๋ฅํฉ๋๋ค.
let id = Symbol("id");
alert(id.description); // id
Symbol ์ค๋ฌด
class Counter {
count = 0;
add() {
return this.count++;
}
get() {
return this.count;
}
}
class BetterCounter extends Counter {
count = function() { ... }; // conflict !!!!!!!!!!!!!!!!!
...
}
์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด์ ๋ด๋ถ ํ๋๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋ public์ ๋๋ค.
๊ทธ ๋ง์ ๋๊ตฌ๋ ๋ด๋ถ ํจ์, ๊ฐ์ ๋ฎ์ด ์ธ ์ ์๋ค๋ ๋ง์ด์ฃ .
โ
์ ์ฝ๋์์ , ๊ทธ๋ฅ count๋ผ๋ ๋ฌธ์์ด ํค ๊ฐ์ ์ฌ์ฉํ๋๋ฐ, ์ด๊ฑฐ๋ฅผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ์ธ๋ถ์ ๋ฐฐํฌ๋ฅผ ํ ๋ฒ ํ๋ค๊ณ ์์์ ํด๋ณด์ธ์.
๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ์ด๋ค ํ๋ก๊ทธ๋๋จธ๊ฐ ์ด Counter ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฐ์์ ์์์ ํตํด ๊ธฐ๋ฅ์ ํ์ฅํด์ ์ฌ์ฉํ๋ ค๊ณ ํ ๋, ๋ณดํต ๋ด๋ถ ์ฝ๋๊ฐ ์ด๋ป๊ฒ ๋์ด์๋์ง ๋ชจ๋ฅด๊ธฐ๋๋ฌธ์ ๊ทธ ํ๋ก๊ทธ๋๋จธ๋ ์์ฐ์ค๋ฝ๊ฒ ๋ด๋ถ ๋ณ์๋ฅผ ํ๋ ์ ์ธํ๋๋ฐ ์ด๋ฆ์ count๋ผ๊ณ ์ง์๋ค๊ณ ์์ํด ๋ด ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ฝ๋๋ฅผ ์์ฑํ๊ณ ๋๋ ค๋ณด๋๋ฐ ๋์์ด ์ ์๋ ๊ฒ๋๋ค.
๋ก์ง์ ๋ฌธ์ ๊ฐ ๋ ๊ฒ์ ์๋๋ฐ ๋ฌธ์ ๊ฐ ๋๋ ์ด์ ๋, ๋ถ๋ชจํด๋์ค ์ธ์คํด์ค count๋ณ์๊ฐ ๋ฎ์ด์์์ ธ์๊ธฐ ๋๋ฌธ์ด์ฃ .
โ
๋ฐ๋ผ์, ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ์๋ฐฉ ํ๊ธฐ ์ํด, Symbol์ ์จ์ '์จ๊น ์ฒ๋ฆฌ'๋ฅผ ํด์ฃผ๋ ๊ฒ์ ๋๋ค. ์ฌ๋ณผ์ ์ด์ฉํด์ ๋ด๋ถ ๋ณ์๋ฅผ ์ ์ธํ๋ค๋ฉด ์ค๋ณต๋๋ ๊ฑฑ์ ์ ํ ํ์๊ฐ ์์ต๋๋ค.
โ
์ฌ๋ณผ์ ์ ์ธํ๋ ์๊ฐ ์ด๊ฑด private ํ๋๋ผ๊ณ ํ๋ก๊ทธ๋๋จธ์๊ฒ ๊ฐ๋ ์ฑ์ ๋์ฌ์ค ๋ฟ๋ง ์๋๋ผ, ์ธ์คํด์ค ์ค๋ณต์ ํผํ ์ ์์ต๋๋ค.
const count = Symbol();
class Counter {
[count] = 0;
add(){
this[count] += 1;
return this;
}
get(){
return this[count];
}
}
const counter = new Counter();
console.log(counter.get()); // 0
counter.add().add().add();
console.log(counter.get()); // 3
Symbol.iterator
์ด๋ค ๊ฐ์ฒด๊ฐ Symbol.iterator๋ฅผ ํ๋กํผํฐ key๋ก ์ฌ์ฉํ ๋ฉ์๋ ๊ฐ์ง๊ณ ์์ผ๋ฉด ์๋ฐ์คํฌ๋ฆฝํธ ์์ง์ ์ด ๊ฐ์ฒด๊ฐ ์ดํฐ๋ ์ด์ ํ๋กํ ์ฝ์ ๋ฐ๋ฅด๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๊ณ ์ดํฐ๋ ์ดํฐ๋ก ๋์ํ๋๋ก ํฉ๋๋ค.
Symbol.iterator๋ฅผ ํ๋กํผํฐ key๋ก ์ฌ์ฉํ์ฌ ๋ฉ์๋๋ฅผ ๊ตฌํํ๊ณ ์๋ ๋นํธ์ธ ๊ฐ์ฒด(๋นํธ์ธ ์ดํฐ๋ฌ๋ธ)๋ ์๋์ ๊ฐ์ต๋๋ค. ์๋์ ๊ฐ์ฒด๋ค์ ์ดํฐ๋ ์ด์ ํ๋กํ ์ฝ์ ์ค์ํ๊ณ ์์ผ๋ฉฐ ์ดํฐ๋ ์ดํฐ๋ฅผ ๋ฐํํฉ๋๋ค.
์ดํฐ๋ ์ดํฐ๋ฅผ ๋ฐํํ๋ค๋ ๋ป์, for of๋ฌธ์ผ๋ก ์์ ํ๋์ฉ ์ํ๊ฐ ๊ฐ๋ฅํ๋ค๋ ๋ง์ ๋ปํฉ๋๋ค.
Array
Array.prototype[Symbol.iterator]
โ
String
String.prototype[Symbol.iterator]
โ
Map
Map.prototype[Symbol.iterator]
โ
Set
Set.prototype[Symbol.iterator]
โ
DOM data structures
NodeList.prototype[Symbol.iterator] HTMLCollection.prototype[Symbol.iterator]
โ
arguments
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 }
Symbol ์ ์์
๊ฐ์ฒด์ key๊ฐ Symbol์ผ ๊ฒฝ์ฐ, for..in ๋ฐ๋ณต๋ฌธ์์๋ ๋ฐฐ์ ๋๋ค.
Object.keys ๋ฅผ ์ฌ์ฉํด๋ ํค๊ฐ ์ฌ๋ณผ์ธ ํ๋กํผํฐ๋ ๋ฐฐ์ ๋๋ค.
๋น์ฐํ ์จ๊น ์ฒ๋ฆฌ ๊ธฐ๋ฅ ํ๋ ์๋ฃํ ์ด๋๊น
โ
'์ฌ๋ณผํ ํ๋กํผํฐ ์จ๊ธฐ๊ธฐ(hiding symbolic property) ์์น'
์ธ๋ถ ์คํฌ๋ฆฝํธ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ Symbolํ ํค๋ฅผ ๊ฐ์ง ํ๋กํผํฐ์ ์ ๊ทผ ํ์ง ๋ชปํจ
Object.assign์ ์์ธ?
Symbol ํค๋ฅผ ๊ฐ์ง ํ๋กํผํฐ๋ ๋ณต์ฌํ๋ค.
# Reference
https://poiemaweb.com/es6-symbol
https://ko.javascript.info/symbol
https://pks2974.medium.com/javascript%EC%99%80-%EC%8B%AC%EB%B3%BC-symbol-bbdf3251aa28