1. Symbol
1-1. 들어가기 전에 다시한번 정리하는 데이터 Type 분류
– Primitive
Value –
값 기본형
임
- number
- string
- boolean
- null
- undefined
- Symbol
– Reference
Value –
값 참조형
임
- object
- array
- function
- Map
- Set
- WeakMap
- WeakSet
기본형
값과 참조형
값의 가장 큰 차이는?
- 주소값을 가진다 (X) 둘 다 주소값을 가짐
- 각자 가지는 그 주소값에
값이 그대로
들어오느냐,값이 별개의 데이터로
따로 있느냐의 차이
Es6에서 새로 등장한 type
1-2. Symbol
탄생 목적 :
비공개 멤버
에 대한 요구를 충족시키기 위해!
- primitive value
- 유일무이하고 고유한 존재
- 비공개 멤버에 대한 needs에서 탄생
- Js에는 private member가 없어서
- 기본적인 열거대상에서 제외
- for in 문처럼 값을 순회하면서 출력하려고 할 때
접근불가
- for in 문처럼 값을 순회하면서 출력하려고 할 때
- 암묵적 형변환 불가
1-2-1. 암묵적 형변환
다른 타입의 경우
'a' + 1
// "a1"
true + 1
// 2
문자열 + 숫자
숫자가 문자열로 암묵적 형변환 됨
boolean + 숫자
boolean이 숫자로 암묵적 형변환 됨
Symbol의 경우
Symbol() + 1
// error! can not convert a Symbol value to a number
Symbol() + 'a'
// error! can not convert a Symbol value to a string
형변환 불가능
const a = Symbol()
const b = Symbol()
a === b
// false
a == b
// false
타입+값 일치 비교도 X
값 일치 비교도 X
완전히 다른 값이라서 비교가 불가능
const a = Symbol('a')
const b = Symbol('a')
a === b
// false
a == b
// false
Symbol로 값을 똑같이 써넣어도 다름.
Symbol은 생성 시마다
완전히 다른 값!
const a = Symbol('a')
const b = Symbol('a')
const c = b
b === c
// true
위치를 참조시키는 건 같다.
const x = () => {
const a = Symbol('a')
return {
[a]: 10
// 변수에 접근하기 위한 대괄호 표기법,
// 객체의 literal 생성시에도 사용 가능하다
}
const y = x();
y
// { Symbol(a): 10 }
y.a
//undefined
y['a']
//undefined
y[Symbol('a')]
//undefined
}
y.a
y[‘a’]
y[Symbol(‘a’)]
이렇게는 a의 값에 접근할 수 없음!
가능한 유일한 방법은?
변수 a를 접근하는 곳에서만 해당 값을 정확히 쓸 수 있음.
a에 접근하지 못하는 곳에서는 symbol a라는 값을 알아낼 수 없다.
const x = () => {
const a = Symbol('a')
return {
[a]: 10,
a: a
}
const y = x();
y
// { a:Symbol(a), Symbol(a): 10 }
y.a
// Symbol(a): 10
y['a']
// 10
}
변수 a에 접근하기 위해서,
함수 x를 return할 때, a라는 property를 다시 내려받음
그럼 a라는 property에 심볼이 있으니 값 자체를 알 수 있다.
이제 a로 접근할 수 있음!
이때의 x함수는 은닉화에 성공한 함수.
값은 쓸 수 있고 노출은 되어있지만
퍼블릭한 상태로 접근할 수 있는 권한을
주느냐, 주지 않느냐에 따라 다르다!
- 퍼블릭한 상태로
접근 권한 O
const x = () => {
const a = Symbol('a')
return {
[a]: 10,
a: a
}
a라는 property를 밖으로 빼줌
=> 값에 접근할 수 있다
- 퍼블릭한 상태로
접근 권한 X
const x = () => {
const a = Symbol('a')
return {
[a]: 10
}
a라는 property를 밖으로 빼주지 않음
=> 보이기만 하고 값에 접근할 수 없다
y를 통해 객체에 접근할 수 있지만,
객체에 있는 Symbol(a)라는 Property엔 접근불가
Reflect.ownKeys(y)
접근할 수 있는 방법 있긴 함
const x = () => {
const a = Symbol('a')
return {
[a]: 10
}
Reflect.ownKeys(y)
// [Symbol(a)]
const b = Reflect.ownKeys(y)
b// [Symbol(a)]
y[b[0]]
// 10
하지만 이 방법은 0번째 값이 바뀔 수 있어서 좋지 않음.
1-2-2. new 연산자 사용 불가능
new Symbol()
// error! Symbol is not a constructor
new연산자 사용 불가, 무조건 static한 형식으로 써야함
1-2-3. 문자열 아닌 타입은 toString처리
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)
const sb2 = Symbol(obj)
console.log(sb1)
// Symbol([object object])
obj.toString()
// "[Object Object]"
Symbol([toString]) 처리되어서 Object로 들어감
const sb = Symbol(null)
sb
// Symbol(null)
console.log(typeof sb)
// symbol
null도 문자열로 들어감
typeof로는 symbol이라고 출력됨
1-2-4. 객체의 property의 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)
Symbol로 선언한 property는 열거되지 않음
그러니 이 객체의 식별자 이상으로써의 의미는 지니지 않음.
그래서 주로 상수로 오는 경우가 많음
const obj = {
[Symbol('a')]: 1
}
obj
// { Symbol(a): 1 }
Symbol(a)의 값을 알고있는 변수가 없음.
그래서 이 값을 알아낼 수 있는 방법이 Reflect.ownKeys(y) 말고는 없다!
은닉화를 떠나서 나도 못쓰는 값이 되어버림
1-2-5. 프로퍼티 키로 할당한 심볼 탐색 (접근)
console.log(iu[NAME], suzi[NAME], jn[NAME])
for (const prop in iu) {
console.log(prop, iu[prop])
}
// age 26
Object.keys(iu).forEach(k => {
console.log(k, iu[k])
})
// age 26
Object.getOwnPropertyNames(iu).forEach(k => {
console.log(k, iu[k])
})
// age 26
for in 문으로는 Symbol을 제외한 age만 출력됨.keys
, getOwnPropertyNames
도 마찬가지
Object.getOwnPropertySymbols(iu).forEach(k => {
console.log(k, iu[k])
})
// Symbol(이름) "아이유"
// Symbol(성별) "female"
Reflect.ownKeys(iu).forEach(k => {
console.log(k, iu[k])
})
// age 26
// Symbol(이름) "아이유"
// Symbol(성별) "female"
getOwnPropertySymbols
새로 등장한 메소드. 객체 안에있는 symbol들만 반환해준다.
대신 이건 Symbol만 나오고 age값이 안나옴!
Reflect.ownKeys()
이걸 사용하면 모두 출력이 가능.
1-2-6. private member 만들기
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])
// 이건 _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])
})
그냥 객체를 만들지 않고 즉시실행 함수로 만들어서 함수 스코프를 생성.
그럼 _privateMember1, _privateMember2는 함수 스코프 밖을 벗어날 수 없음
이렇게 Symbol을 활용하면 private member를 흉내낼 수 있다.
🤷🏻♀️ 근데 private member를 왜 하려고 해??
캡슐화
, 바꾸는 실수를 방지하기 위해서.
외부에서 접근 못하게 한다 === 다른사람이, 혹은 내가 바꾸지 못하게 한다
obj는 캡슐화
가 된 것!
이것과는 완전히 정반대인 Symbol.for
가 있다.
1-2-7. Symbol.for
- 공유심볼
- public member! 전역공간에서 공유되는 심볼.
const a = Symbol.for('abc')
const b = Symbol.for('abc')
a === b
// true!
식별하는 방법이, 문자열만 가지고 식별을 한다.
그리고 Symbol.for라고 하는 퍼블릭한 심볼들이 가지는 값을 모아둔 전역공간에 있는 공간이 있음
그 공간에 Symbol.for로 선언한 것들이 다 모여있음!
Symbol.for 를 처음 사용하면 그 값을 공용공간에 저장
을 하고,
이미 사용했으면 그걸 그대로 가져다가 쓴다
.
=> a는 처음 선언
한 거니까 공간에 저장
하고
b는 이미 있는지
찾아보고 있으니까 그대로 가져다가 쓴 것
const COMMON1 = Symbol.for('공유심볼')
const obj = {
[COMMON1]: '공유할 프로퍼티 키값이에요. 어디서든 접근 가능하답니다.'
}
console.log(obj[COMMON1])
// 공유할 프로퍼티 키값이에요. 어디서든 접근 가능하답니다.
const COMMON2 = Symbol.for('공유심볼')
console.log(obj[COMMON2])
// 공유할 프로퍼티 키값이에요. 어디서든 접근 가능하답니다.
console.log(COMMON1 === COMMON2)
// true
Symbol.keyFor
const UNCOMMON = Symbol('비공유심볼') const commonSymbolKey1 = Symbol.keyFor(COMMON1) // 공유심볼 const commonSymbolKey2 = Symbol.keyFor(COMMON2) // 공유심볼 const commonSymbolKey2 = Symbol.keyFor(UNCOMMON) // undefined
Symbol.for에서만 쓸 수 있는 메소드.
변수에 있는 키, 문자열 값을 출력해준다.