27일차 - class, getter, setter, 상속, 복사, 모듈

변승훈·2022년 5월 5일
0

class

  • 구조를 만들어놓고 인스턴스라고 불리우는 객체 데이터를 찍어낼 수 있다.
  • 생성자 함수로만 사용하라고 정의하기 위해 파스칼케이스로 작성 한다.
  • 내부 메소드는 한 번만 만들어진다.
  • class는 "틀"이다.
// 1.
function Hello() {
  this.msg = 'Hello'
}

// this는 사용되는 곳마다 다름
// res1, res2 = instance
const res1 = new Hello() // res1만 있을 때 this.msg의 this = res1
console.log(res1)  // hello {msg: 'Hello!'}

const res2 = new Hello() 
console.log(res2) // hello {msg: 'Hello!'}

const res3 = {
  msg: 'Hello'
  getMsg: function() {  // 메소드
    return this.msg
  }
}
console.log(res.getMsg()) // Hello


const res4 = {
  msg: 'World'
  getMsg: function() {  // 메소드
    return this.msg
  }
}
console.log(res4.getMsg())  // World
console.log(res3.getMsg === res4.getMsg)  // false, 똑같이 생긴 참조형이지만 주소가 다르다.

// 2.
// 객체데이터를 생성자함수(class)형태로 만들게 되면 재활용이 가능하다.
// 객체를 리티럴 방식으로 똑같은 구조를 여러개를 만들어 사용하면 객체를 만드는 만큼 메모리가 낭비가 된다
// 
function Hello() {
  this.msg = 'Hello'
}
Hello.prototype.getMsg = function () {
  return this.msg
}
const res1 = new Hello() 
console.log(res1)  // hello {msg: 'Hello!'} Prototype안에 getMsg존재

const res2 = new Hello() 
console.log(res2) // hello {msg: 'Hello!'} Prototype안에 getMsg존재

console.log(res1.getMsg === res2.getMsg)  // true, 같은 메모리에 있는 함수 데이터를 같은 주소로 바라보고 있다.
  • class 문법은 prototype으로 돌아간다. 지금은 새로운 class문법이 나와 최신 문법에 맞춰 사용한다.
  • class와 prototype은 선언부만 다르고 사용법은 똑같다.
// 3.
// class와 prototype은 선언부만 다르고 사용법은 똑같다.
// class는 틀이고 데이터는 붕어빵 종류이다.
function Hello(str) {
  this.msg = str
}
Hello.prototype.getMsg = function () {
  return this.msg
}
const res1 = new Hello('팥') 
console.log(res1) // {msg: '팥'}

const res2 = new Hello('슈크림') 
console.log(res2) // {msg: '슈크림'}

console.log(res1.getMsg === res2.getMsg)  // true
  • prototype에서 쓰인 this는 인스턴스이다.
// 4. 
Array.prototype.hun = function () {
  console.log('instance: ', this) // this는 인스턴스(prototype안에서 사용하기 때문에)
}

const arr = new Array(1, 2, 3, 4)
// 리터럴방식
const arr = [1, 2, 3, 4]
arr.hun();  // instance: [1, 2, 3, 4]

// 5. 예제를 최신 class 문법 사용
class Hello {
  constructor(str) {  // construtor = class Hello
    this.msg = str
  }
  getMsg () {
    return this.msg
  }
}
const res1 = new Hello('팥') 
console.log(res1) // {msg: '팥'}

const res2 = new Hello('슈크림') 
console.log(res2) // {msg: '슈크림'}

console.log(res1.getMsg === res2.getMsg)  // true

class 사용 예시

// 6.class 사용 예시

// html
<div class="font1">Hello</div>
<div class="font2">World</div>
<button>OK!</button>
// js
class Fonty {
  constructor(selector, options) {
    const defaultOptions = {
      fontSize: '16px',
      fontWeight: '700',
      color: 'black'
    }
    // option 병합
    this.options = {  // 얕은 복사
      ...defaultOptions,  // 전개연산자는 {}를 날려준다. 위의 fontSize ~ color까지 {}를 날리고 들어온다.
      ...options
    }
    // 요소를 검색
    this.els = document.querySelectorAll(selector)  // querySelectorAll 로 반환된 결과는 배열 데이터가 아닌 배열과 유사하게 생긴 NodeList이다.
    this.setStyle(this.options)
  }


  // { color: 'red', fontSize: '20px'} 
  setStyle(style) {
    this.els.forEach(el => {  // this는 인스턴스, forEach는 NodeList의 메소드이다.
      Object.assign(el.style, style)
    })
  }
  // 원래 prototype 메소드
  // static을 사용하여 정적 메소드로 선언
  static setRed(instance) {
    instance.setStyle({
      color: 'red'
    })
  }
  static getFamlies() {
    return ['serif', 'sans-serif', 'monospace', 'cursive']
  }
  // Getter, 메소드가 아닌 속성, 
  get fontSize() {
    console.log('in Getter: ', this.options.fontSize)
    return this.options.fontSize
  }
  // Setter
  set fontSize(value) {
    console.log('in Setter: ', value)
  }
}


// 사용예시!
new Fonty('.font1', {
  fontSize: '30px',
  fontWeight: '400',
  color: 'red'
})

const fonty = new Fonty('.font2', {
  fontSize: '30px',
  fontWeight: '400',
  color: 'green'
})

document.querySelector('button')
  .addEventListener('click', () => {
  // fonty.setStyle({
  //   color: 'yellowgreen'
  // })

  // Fonty.setRed(fonty)

  console.log(fonty.fontSize) // 30px
  fonty.fontSize = '99px'
})

console.log(Fonty.getFamlies)// ['serif', 'sans-serif', 'monospace', 'cursive']

getter, setter

  • getter: 획득자로 표현된다. 쉽게 말하자면 값을 가져와 읽을 때 사용한다.
  • setter: 설정자로 표현된다. 쉽게 말하자면 값을 할당하려 할 때 사용한다.
// getter, setter
class User {
  constructor(first, last) {
    this.first = first
    this.last = last
  }
  get fullName() {
    return `${this.first} ${this.last}`
    // return this.first + ' ' + this.last
  }
  set fullName(v) {
    // console.log(v.split(' ')) //   ['tomas', 'Miller']
    // const res = v.split(' ')
    // this.first = res[0]
    // this.last = res[1]
    const [first, last] = v.split(' ')
    this.last = first
    this.last = last
  }
}

const hun = new User('Hun', 'Byeon')
console.log(hun.fullName) // Hun Byeon
hun.fullName = 'tomas Miller'
console.log(hun.fullName) // tomas Miller

상속

  • 클래스를 상속하면 클래스를 다른 클래스로 확장할 수 있다.
  • 이때 extends를 사용하며 기존에 존재하던 기능을 토대로 새로운 기능을 만들수 있다.
  • super()는 상위 부모의(extends한) 함수를 호출할 때 사용한다.
class User {
  first
  last
  constructor() {
    // No use
  }
  get fullName() {
    return `${this.first} ${this.last}`
  }
  set fullName(v) {
    const [first, last] = v.split(' ')
    this.last = first
    this.last = last
  }
}

class Hun extends User {
  constructor () {
    super() // User의 constructor()
    this.first = 'Hun'
    this.last = 'Byeon'
  }
}

const hun = new Hun()
console.log(hun.fullName)

얕은 복사, 깊은 복사

  • 얕은 복사: 껍데기만 복사하고 안의 내용은 복사하는 개념이 아니다.
  • 깊은 복사: javascript로 순수하게 하기엔 힘들어 loadash를 이용하여 복사를 한다, 복사 대상 그대로 복사를 한다고 보면 된다.
<!-- lodash -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
// 얕은 복사
const ref = {
  a: 1,
  b: 'abc',
  c: [
    { x:1, y:2 },
    { x:1, y:2 },
    { x:1, y:2 }
  ]
}

const refClone = {
  ...ref
}

console.log(ref === refclone) // false
refClone.c[0].x = 999;

// 껍데기만 복사하고 안에는 복사가 아니다.
console.log(ref)  // x: 999


// 깊은 복사: lodash이용
const refClone = _.cloneDeep(ref)

모듈 가져오기, 내보내기

  • 가져오기(import), 내보내기(export) => 모듈(module)

  • export: 름을 가지는 내보내기, 이름 내보내기(다중 출구)
    중복이 가능하며 import에서 hello로 밖에 가져오지 못한다.

  • import: 기본내보내기(이름을 꼭 맞춰줄 필요 없다), { 이름내보내기(이름을 꼭 맞춰줘야 한다) } from ''

  • ★모듈 안에서의 this는 undefined이다. 모듈은 틀에 갇혀져 버리기 때문이다.

<!-- type="module"이 있어야 import, export를 사용할 수 있다. -->
<script type="module" defer src="./main.js"></script>
// 가져오기(import), 내보내기(export) => 모듈(module)
// test.js: index.html에 연결이 되어있지 않음, 모듈
// 1. 이름을 가지는 내보내기, 이름 내보내기(다중 출구)
// 중복이 가능하며 import에서 hello로 밖에 가져오지 못한다.
export function hello() {
  console.log('Hello world')
}

export function world = {
  a: 1,
  b: 2
}

// 기본 내보내기 (단일 출구)
// export default 데이터
export default function hun() {
  console.log('guakakakakakakaka')
}

// main.js: index.html에 연결이 되어 있음
// import 기본내보내기(이름을 꼭 맞춰줄 필요 없다), { 이름내보내기(이름을 꼭 맞춰줘야 한다) } from ''
import hun, { hello, world } from './test.js'

hello();

// ★모듈 안에서의 this는 `undefined`이다. 모듈은 틀에 갇혀져 버리기 때문이다.
profile
잘 할 수 있는 개발자가 되기 위하여

0개의 댓글