Skip to content

Latest commit

 

History

History
executable file
·
307 lines (241 loc) · 8.07 KB

11. symbol.md

File metadata and controls

executable file
·
307 lines (241 loc) · 8.07 KB

11. Symbol

11-1. Symbol

  • 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

11-1-1. 만들기

  • 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)

11-1-2. 객체 프로퍼티의 키로 활용

  • 심볼을 객체의 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)

11-1-3. 프로퍼티 키로 할당한 심볼 탐색 (접근)

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])
})

11-1-4. 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]) // 접근안됨 

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])
})

11-2. Symbol.for - 공유심볼

  • 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])

11-3. 표준 심볼

  • 기존에 내장 메소드(원래 있던 기능)를 제어할 수 있는 권한을 줄 수 있는 공식적 방법을 고민하다가 나온 개념
  • 기존에는 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문과 관련.

표준 심볼들의 의의: 해당 심볼을 재정의함으로써 기존에는 표준객체 내부 전용이던 기능들을 개발자의 입맛에 맞게 바꾸어 쓸 수 있게 되었음.

스코프에 적용될 것인가, 적용되지 않을 것인가를 결정할 수 있음