[자바스크립트] 클래스

박은정·2021년 11월 3일
0

자바스크립트

목록 보기
13/25
post-thumbnail

클래스

객체를 효율적이고 안전하게 만들기 위해 만들어진 문법

객체 지향 Object Oriented

C를 제외한 모든 프로그래밍 언어는 객체지향이라는 패러다임을 기잔으로 만든 프로그래밍 언어이다

객체 지향 패러다임

객체를 우선적으로 생각해서 프로그램을 만든다는 방법론이다
객체 지향 프로그래밍 언어들은 클래스 class 라는 문법으로 객체를 효율적이고 안전하게 만들어 객체 지향 패러다임을 쉽게 프로그래밍에 적용할 수 있도록 도와준다

추상화

프로그램에 필요한 요소만 사용해서 객체를 표현하는 것을 추상화 abstraction 이라고 한다
포괄적인 사전적 의미로는 복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념과 기능을 간추려내는 것을 말한다

같은 형태의 객체 만들기

학생 성적 관리 프로그램을 만든다고 할 때,
객체 : 학생
추상화 : 학생들로부터 성적 관리에 필요한 공통사항을 추출하는 것

성적 총합을 구하는 기능과 평균을 구하는 기능은 여러 프로그램에서 쓰일 수 있기 때문에
단순하게 계산하는 것보다 함수로 만들어 놓으면 더 편하게 활용할 수 있다

객체의 기능을 메서드로 추가하기

객체의 수가 늘어나면 함수 이름 충돌이 발생할 수 있고,
매개변수에 어떤 종류의 객체를 넣을지 몰라 함수를 사용하는 데 혼동이 있을 수 있는데
이러한 문제를 해결하기 위해서는 getAverageOdStudent() 처럼 의미를 알 수 있게 길게 작성할 수도 있고 함수를 메서드로써 객체 내부에 넣어서 활용할 수도 있다

const students = []
student.push({ })
student.push({ })
student.push({ })
student.push({ }) // 내용 생략

// students 배열 내부의 객체 모두에게 메서드 추가
for (const student of students) {
  student.getSum = function () {
    return this.국어 + this.영어 + this.수학 + this.과학
  }
  
  student.getAverage = function () return this.getSum() / 4
}

반복문을 통해 students 배열의 각각의 요소에 메서드를 추가하면
함수 이름 충돌도 발생하지 않고, 함수를 잘못 사용하는 경우도 줄일 수 있다

함수를 여러개 만들면 좋은 점

객체를 하나씩 만들 때와 비교해서

  • 오탈자의 위험이 줄어든다
  • 코드를 입력하는 양이 크게 줄어든다
  • 속성과 메서드를 한 함수 내부에서 관리할 수 있기 때문에 유지보수에 용이하다

하지만, 객체별로 여러 메서드를 생성하게 되면 함수라는 기본 자료형보다 무거운 자료형이 여러번 생성하게 된다

클래스 선언하기

객체 지향 프로그램

객체들을 정의하고 그러한 객체를 활용해서 프로그램을 만드는 것

클래스 & 인스턴스 (객체)

클래스 : 객체를 만들 때 수많은 지원을 하는 대신 많은 제한을 거는 문법
프로토타입 : 제한을 많이 하지 않지만, 지원하는 것도 별로 없는 문법

class 클래스이름 { }

인스턴스 :클래스를 기반으로 만든 객체, 그냥 객체라고도 한다

인스턴스 생성

new 클래스이름 ()
  • 클래스 : 객체를 만드는 함수와 비슷한 것
  • 인스턴스 (객체) : 객체를 만드는 함수로 만든 객체와 비슷한 것

생성자

new Student() 라는 코드를 보면 Student 뒤에 함수처럼 괄호가 있다
객체가 생성될 때 호출되는 생성자 constructor 라는 이름의 함수이다

class 클래스이름 {
  constructor() { 생성자 코드 }
}

여기서 메서드의 이름을 constructor 라고 지정했지만,
constructor 라는 이름으로 사용하는 것이 아니라 new Student() 처럼 클래스 이름으로 호출한다

생성자는 클래스를 기반으로 인스턴스를 생성할 때 처음 호출되는 메서드이기 때문에
생성자에서는 속성을 추가하는 등 객체의 초기화 처리를 한다

class Student {
  constructor (이름, 국어, 영어, 수학, 과학) {
    this.이름 = 이름
    this.국어 = 국어
    this.영어 = 영어
    this.수학 = 수학
    this.과학 = 과학
  }
}

// 객체 선언
const students = []
student.push(new Student('구름', 87, 43, 55, 54, 21))
student.push(new Student('별이', 87, 43, 55, 54, 21))
student.push(new Student('겨울', 87, 43, 55, 54, 21))
student.push(new Student('바다', 87, 43, 55, 54, 21))

react class 형 컴포넌트와 비슷한 것 같다

메서드 method

class Student {
  constructor (이름, 국어, 영어, 수학, 과학) {
    this.이름 = 이름
    this.국어 = 국어
    this.영어 = 영어
    this.수학 = 수학
    this.과학 = 과학
  }
  
  getSum () return this.국어 + this.영어 + this.수학 + this.과학
  getAverage () return this.getSum() / 4
  toString () return `${this.이름}\t${this.getSum()}점\t${this.getAvreage()}점\n`
}


// 객체 선언
const students = []
student.push(new Student('구름', 87, 43, 55, 54, 21))
student.push(new Student('별이', 87, 43, 55, 54, 21))
student.push(new Student('겨울', 87, 43, 55, 54, 21))
student.push(new Student('바다', 87, 43, 55, 54, 21))

이렇게 메서드를 만들게 되면 내부적으로 메서드가 중복되지 않고 하나만 생성되어 활용할 수 있게 된다

상속

class Rectangle {
  constructor (width, height) {
    this.width = width
    this.height = height
  }
  
  getPerimeter () return 2 * (this.width + this.height)
  getArea () return this.width + this.height
  
  // extends Rectangle : Square 클래스가 Rectangle 클래스의 자식 클래스이다
  class Square extends Rectangle { 
    constructor (length) {
      super (length, length) // 부모의 생성자 함수를 호출하는 함수
    }
  }
}

Square 클래스에서 getPerimeter() 메서드와 getArea() 메서드를 선언하지 않았어도
부모 클래스인 Rectangle 클래스에서 속성과 메서드를 상속 받으면 사용할 수 있다

super()

super() 함수는 부모의 생성자를 나타내는 함수이다
super(length, length) 를 호출하면 Rectangle 클래스의 constructor (width, height) 가 호출되어 width, height 속성이 들어가게 된다

private 속성과 메서드

프레임워크나 엔진을 개발자가 사용할 때, 의도대로 사용하지 않으면 문제가 발생할 수 있는데
조건문을 활용해서 의도대로 사용하지 않을 때 예외를 발생시켜 사용자에게 인지시켜주는 방법이 있다

이처럼 클래스 사용자가 클래스 속성이나 메서드를 의도하지 않은 방향으로 사용하는 것을 막아
클래스의 안정성을 확보하기 위해 나온 문법이 private 속성과 메서드이다

class 클래스이름 {
  #속성이름
  #메서드이름 () { }
}

속성과 메서드 이름 앞에 # 을 붙이기만 하면 된다

#이 붙어있는 속성과 메서드는 모두 private 속성과 메서드이다

private 속성은 사용하기 전에 미리 외부에 어떤 속성을 private 속성으로 사용하겠다고 선언해야줘야 한다

class Square {
  #length
  
  constructor (length) {
    if (length <= 0 ) throw '길이는 0보다 커야 합니다.'
    this.#length = length
  }

  getPerimeter () return 4 * this.#length
  getArea () return this.#length * this.#length
}

private 속성으로 변경하면 클래스 외부에서는 해당 속성에 접근할 수 없기 때문에
클래스 사용자가 클래스를 잘못 사용하는 문제를 줄일 수 있다

게터 & 세터

private 속성을 상황에 따라 읽고 쓸 수 있는 메서드를 제공한다

class Square {
  #length
  
  constructor (length) {
    this.#length = length
  }

  setLength (value) {
    // 함수를 사용하기 때문에 내부에서 예외 처리를 할 수 있다
    if (value <= 0) throw '길이는 0보다 커야 합니다'
    this.#length = value
  }
  getLength (value) return this.#length
  getPerimeter () return 4 * this.#length
  getArea () return this.#length * this.#length
}

여기서는 새로 setLength() 메서드와 getLength() 메서드가 새로 생성된 것을 확인할 수 있는데

게터

get~ 메서드처럼 속성 값을 확인할 때 사용하는 메서드를 게터 getter 라고 부르고

세터

set~ 메서드처럼 속성에 값을 지정할 때 사용하는 메서드를 세터 setter 라고 부른다

게터와 세터는 처음부터 무조건 있어야 되는 것이 아니라 필요한 경우에만 사용한다
만약 사용자가 값을 읽는 것을 거부하겠다면, 게터를 만들지 않아도 된다
사용자가 값을 지정하는 것을 거부한다해도 세터를 만들지 않아도 된다
속성에 접근하지 못하게 둘 다 막아도 된다

class 클래스이름 {
  get 메서드이름 () returnset 메서드이름 (value) {}
}

static 속성과 메서드

디자인 패턴

프레임워크 개발자들이 더 효율적으로 프레임워크를 개발할 수 있게 다양한 패턴을 고안했는데
이러한 패턴을 디자인 패턴이라고 한다

원래 자바스크립트에 클래스라는 기능이 없었지만, 여러 디자인 패턴을 활용하기 위해 클래스 문법들이 계속 추가된 것 처럼 static 속성과 static 메서드도 새로 생겼다고 볼 수 있다

class 클래스이름 {
  static 속성 =static 메서드 () { }
}

static 속성과 메서드는 인스턴스를 만들지 않고 사용할 수 있는 속성과 메서드이다
일반적인 변수와 함수처럼 사용이 가능하다
클래스 이름 뒤에 점을 찍고 속성과 메서드를 사용한다

클래스이름.속성
클래스이름.메서드()
class Square {
  #length
  // private 특성과 static 특성은 같이 적용할 수도 있다
  static #counter = 0
  static get counter () return Square.#counter
  
  constructor (length) {
    this.length = length
    Square.#counter += 1
  }

  static perimeterOf (length) return length * 4
  static areaOd (length) return length * length
  get length () return this.#length
  get perimeter () return this.#length * 4
  get area () return this.#length * this.#length
  
  set length (length) {
    if (length <= 0) throw '길이는 0보다 커야 합니다.'
    this.#length = length
  }
}

#counter 라는 이름의 static 속성과 counter() 라는 이름의 메서드(게터)를 만들었다
#counter 라는 속성은 Square 객체의 생성자가 호출될 때마다 1씩 증가하도록 했는데
이를 활용해서 현재까지 Square 객체가 몇 개 생성 되었는지 확인할 수 있다

perimeterOf() 메서드와 areaOf() 메서드 : Square 객체를 생성하지 않고도 둘레와 넓이를 간단하게 구할 수 있게 하는 메서드이다

이처럼 변수와 함수를 클래스 내부에 작성하게 되면

  • 어떤 속성과 함수가 클래스 내부에 귀속되어 있는지 확인가능
  • private 특성과 게터, 세터를 부여해서 → 더 안전하게 사용할 수 있다

오버라이드

부모가 가지고 있는 함수를 자식에서 다시 선언해서 덮어쓰는

// 클래스 선언
class LifeCycle {
  call () {
    this.a()
    this.b()
    this.c()
  }
  
  a () console.log(`a() 메서드를 호출합니다.`)
  b () console.log(`b() 메서드를 호출합니다.`)
  c () console.log(`c() 메서드를 호출합니다.`)
}

class Child extends LifeCycle { // 자식클래스
  a () console.log('자식의 a() 메서드입니다.')
}

// 인스턴스 생성
new Child().call()

LifeCycle 라는 클래스에는 a(), b(), c()라는 메서드가 있고 call()이라는 이름의 메서드에서 이를 호출한다
call() 메서드에서 a() 메서드를 실행하는데 a() 메서드가 덮여쓰여졌기 때문에 새로운 a() 메서드의 내용을 출력한 것이다

부모에 있던 내용 가져오기

만약 부모에 있던 메서드의 내용도 사용하고 싶다면 super.메서드() 형태의 코드를 사용한다

// 클래스 선언
class LifeCycle {
  call () {
    this.a()
    this.b()
    this.c()
  }
  
  a () console.log(`a() 메서드를 호출합니다.`)
  b () console.log(`b() 메서드를 호출합니다.`)
  c () console.log(`c() 메서드를 호출합니다.`)
}

class Child extends LifeCycle { // 자식클래스
  a () {
    super.a() // 부모의 a() 메서드 실행
    console.log('자식의 a() 메서드입니다.')
}

// 인스턴스 생성
new Child().call()

자식 클래스에서 동일한 이름의 메서드를 호출할 때
super.a()도 호출하고 새로운 코드도 실행 하기 때문에 두 개의 문장이 모두 실행된다

오버라이드 예시 ⚠️

덮어씌우는 거랑 개념이 먼 것같은데 무슨 말일까..?

자바스크립트의 모든 객체는 toString() 이라는 메서드를 가진다
이는 자바스크립트가 Object 라는 최상위 클래스를 가지며, 어떤 클래스를 만들어도 자동으로 Object 클래스를 상속받게 되어서 발생하는 현상이다

toString() 이라는 이름으로 메서드를 만들면 Object 클래스에 있던 toString() 메서드를 오버라이드하게 되는 것이다

자바스크립트는 내부적으로 어떤 객체를 문자열로 만들 때 toString() 메서드를 호출한다
따라서 toString() 메서드를 오버라이드하면 내부적으로 문자열로 변환되는 형태를 바꿀 수 있다

profile
새로운 것을 도전하고 노력한다

0개의 댓글