- primitive value! => 유일무이하고 고유한 존재.
- 비공개 멤버에 대한 needs에서 탄생.
- 기본적인 열거대상에서 제외.
- 암묵적 형변환 불가.
const x = () => {
const a = Symbol('a');
return {
[a]: 10,
};
};
const y = x();
y[Symbol(a)]; // 로 접급하면 ReferenceError 뜸
y[Symbol('a')]; //로도 접근안됨
// y를 조회하면 값이 뻔하게 보이지만 접근은 불가
// 가능한 접근은 a라는 변수에 접근 가능해야함
// 접근을 가능하게 하기 위해 라는 변수를 담아 함께 리턴
const x = () => {
const a = Symbol('a');
return {
[a]: 10,
a,
};
};
y.a // Symbol(a), 접근 가능,
// a라는 변수를 제공하지 않으면, 10에 접근이 불가능하므로 프로퍼티 은닉화 성공
// 그러나 우회로가 있음
const keyArr = Reflect.ownKeys(y)
const bypass = keyArr[1];
y[bypass] // 10
// 런타임 환경의 코드에서 Reflect.ownkeys를 써서 접근하는 것은 사실상 어려움
// 그러므로 은닉화에 성공했다고 보는편임
// 아래처럼 만들면 누구도 접근할 수 없는 프로퍼티가 생성됨
const obj = {
[Symbon('a')]: 1
}
// 의미없는 은닉화
obj
-
Symlbol
은 new 연산자를 쓰지 않음 -
Symbol([string])
: 문자열이 아닌 타입은 자동으로 toString 처리.( 스트링이 와도 되독 와도됨 )
const sb1 = Symbol()
const sb2 = Symbol()
console.log(sb1, sb2)
console.log(sb1 === sb2) //false
const sb1 = Symbol('symbol')
const sb2 = Symbol('symbol')
console.log(sb1, sb2)
console.log(sb1 === sb2) // false
const obj = { a: 1 } // 문자열 이 안넣어지면?
const sb1 = Symbol(obj) // 자동으로 toString 이 먹혀서 Symbol(object Object)가 됨
const sb2 = Symbol(obj)
console.log(sb1, sb2)
console.log(sb1 === sb2)
const sb = Symbol(null)
console.log(typeof sb)
- 심볼을 객체의 key로 활용할 때는
[]
대괄호 표기법 활용해야만 함
const NAME = Symbol('이름')
const GENDER = Symbol('성별')
const iu = {
[NAME]: '아이유',
[GENDER]: 'female',
age: 26
}
const suzi = {
[NAME]: '수지',
[GENDER]: 'female',
age: 26
}
const jn = {
[NAME]: '재남',
[GENDER]: 'male',
age: 30
}
console.log(iu, suzi, jn)
console.log(iu[NAME], suzi[NAME], jn[NAME])
for (const prop in iu) {
console.log(prop, iu[prop])
}
Object.keys(iu).forEach(k => {
console.log(k, iu[k])
})
Object.getOwnPropertyNames(iu).forEach(k => {
console.log(k, iu[k])
})
Object.getOwnPropertySymbols(iu).forEach(k => {
console.log(k, iu[k])
})
// 이 부분으로만 접근 가능
Reflect.ownKeys(iu).forEach(k => {
console.log(k, iu[k])
})
const obj = (() => {
const _privateMember1 = Symbol('private1')
const _privateMember2 = Symbol('private1')
return {
[_privateMember1]: '외부에서 보이긴 하는데 접근할 방법이 마땅찮네',
[_privateMember2]: 10,
publicMember1: 20,
publicMember2: 30
}
})()
console.log(obj)
console.log(obj[Symbol('private1')]) // 접근안됨
console.log(obj[_privateMember1]) // 접근안됨
for (const prop in obj) {
console.log(prop, obj[prop])
}
Object.keys(obj).forEach(k => {
console.log(k, obj[k])
})
Object.getOwnPropertyNames(obj).forEach(k => {
console.log(k, obj[k])
})
// 물론 아래 방법들로는 접근 가능하나... 번거롭기도 하고 정상적인 접근이라고 보기 어려움
Object.getOwnPropertySymbols(obj).forEach(k => {
console.log(k, obj[k])
})
Reflect.ownKeys(obj).forEach(k => {
console.log(k, obj[k])
})
- public member! 전역공간에서 공유되는 심볼.
- 기존 Symbol과 반대로 작동함
- 그럼 공유심볼을 어디서 많이쓰나?
- 프로젝트 진행시에 여기저기서 사용되는 상수값들을 쓸 때 사용할 가능성이 보임
- Sass로 보면 variable 선언해서 여기저기 쓰는 것 생각해보면 됨
// 공유 심볼
Symbol() // 매번 새로운 값을 만들고, 이 값을 정확히 알아야만 접근 가능함
Symbol.for()
// 인자로 문자열을 반드시 입력해야 하며 없으면 undefined
const a = Symbol.for("abc"); //식별시에 for 함수의 인자로 전달된 인자값으로 식별함
const b = Symbol.for("abc"); //Symbol.for()로 생성된 심볼들이 모여 저장되는 별도의 전역공간이 존재함
// 해당 전역공간에 이미 같은 인자값으로 전달된 심볼이 존재하면 그 심볼을 가져다 쓰고, 없으면 새로 생성함
// 그렇기 때문에 아래값이 True 가 됨, 단 해당 값도 Primitive 값임
a === b // true
// 활용 예제, Redux쪽에서 많이 씀
const keys = ['ADD_TOD', 'DELETE_TODO'];
const Constants = {};
keys.forEach(v => {Constants[Symbol.for(v)] = v})
const COMMON1 = Symbol.for('공유심볼')
const obj = {
[COMMON1]: '공유할 프로퍼티 키값이에요. 어디서든 접근 가능하답니다.'
}
console.log(obj[COMMON1]) // 접근가능
const COMMON2 = Symbol.for('공유심볼')
console.log(obj[COMMON2]) // 접근 가능
console.log(COMMON1 === COMMON2) //true
const UNCOMMON = Symbol('비공유심볼')
const commonSymbolKey1 = Symbol.keyFor(COMMON1) //공유심볼
const commonSymbolKey2 = Symbol.keyFor(COMMON2) // 공유심볼
const commonSymbolKey2 = Symbol.keyFor(UNCOMMON) // undefined 접근 안됨
const obj = (() => {
const COMMON1 = Symbol.for('공유심볼')
return {
[COMMON1]: '공유할 프로퍼티 키값이에요. 어디서든 접근 가능하답니다.'
}
})()
const COMMON2 = Symbol.for('공유심볼')
console.log(obj[COMMON2])
- 기존에 내장 메소드(원래 있던 기능)를 제어할 수 있는 권한을 줄 수 있는 공식적 방법을 고민하다가 나온 개념
- 기존에는 prototype 의 메소드에 접근해서 direct로 바꿔서 사용했음 이제 표준 심볼을 통해 변경해서 사용
- Symbol.hasInstance:
instance instanceof constructor
명령은 내부적으로constructor[Symbol.hasInstance](instance)
으로 동작. - Symbol.isConcatSpreadable:
array의concat
메소드에 인자로 넘길 때 이를 flatten할지 여부를 가리키는 boolean값 (default: true)
const arr = [4, 5, 6]
arr[Symbol.isConcatSpreadable] = true
console.log([1, 2, 3].concat(arr))
arr[Symbol.isConcatSpreadable] = false
console.log([1, 2, 3].concat(arr))
- Symbol.iterator: 추후 다룸.
- Symbol.match: 정규표현식 및 문자열 관련.
- Symbol.replace: 정규표현식 및 문자열 관련.
- Symbol.search: 정규표현식 및 문자열 관련.
- Symbol.species: 파생클래스를 만들기 위한 생성자.
- Symbol.split: 문자열을 나누는 조건 설정.
const str = '이 _ 문자열을 _ 이렇게 _ 나누어주었으면 _ 좋겠어.'
String.prototype[Symbol.split] = function (string) {
let result = ''
let residue = string
let index = 0
do {
index = residue.indexOf(this)
if(index <= -1) {
break
}
result += residue.substr(0, index) + '/'
residue = residue.substr(index + this.length)
} while (true)
result += residue
return result
}
console.log(str.split(' _ '))
- Symbol.toStringTag:
Object.prototype.toString
이 호출되었을 때 어떤 명칭을 반환할 지를 지정 가능.
class Person {
constructor (name) { this.name = name }
}
const jn = new Person('재남')
console.log(jn.toString()) // [object Object]
Person.prototype[Symbol.toStringTag] = 'PERSON'
console.log(jn.toString()) // [obejct PERSON]
- Symbol.unscopables: with문과 관련.
표준 심볼들의 의의: 해당 심볼을 재정의함으로써 기존에는 표준객체 내부 전용이던 기능들을 개발자의 입맛에 맞게 바꾸어 쓸 수 있게 되었음.
스코프에 적용될 것인가, 적용되지 않을 것인가를 결정할 수 있음