(JS) 클래스

woong·2022년 9월 21일
0
post-thumbnail

객체 지향 프로그래밍은 데이터와 기능을 별개로 관리하지 않고 한번에 묶어서 처리한다. 자바스크립트는 엄밀히 객체 지향 프로그래밍 언어는 아니지만, 객체 지향적으로 코드를 짤 수 있다.

클래스 ( class )

객체를 만들 때, ES5까지는 생성자 함수를 사용하여 객체 지향 프로그래밍을 구현했다. 그렇지만 ES6에 들어서 class를 사용하여 객체를 생성할 수 있다. 보다 간편하고 직관적으로 생성할 수 있다. (상속이 가능하다.)
이 때 만들어진 객체를 인스턴스라고 한다.

클래스 정의

객체를 생성할 수 있는 청사진 또는 템플릿

ES5의 생성자 함수와 ES6의 class 키워드를 비교해보자.

*ES5* 생성자함수

function Car (brand, name, color) {
  this.brand = brand ;                
  this.name = name ;
  this.color = color ;
}
Car.prototype.drive = function() {
   console.log (this.name + '가 운전을 시작합니다');
}
*ES6* class 

class Car {
  constructor (brand, name, color) { // 생성자 함수 constructor
   this.brand = brand ;               // 중괄호 안 코드 : 인스턴스를 만들 때 실행되는 코드
   this.name = name ;
   this.color = color ;
  }
  drive() {
    console.log (this.name + '가 운전을 시작합니다');
  }
}
  • ES5에서는 생성자 함수를 정의하는 반면에, ES6에서는 class 키워드를 이용하여 클래스를 정의한다.
  • ES5에서는 키워드 prototype을 사용해야 Car 클래스에 메서드를 정의 할 수 있는데, ES6에서는 class 키워드 내부에 정의한다.
  • 여기서 보이는 함수는 생성자(constructor) 함수인데 클래스 내에 1개만 존재할 수 있다.
  • this는 인스턴스 객체를 의미한다. 위처럼 this에 할당을 하면 인스턴스에 brand,name,color를 할당되어진다.
  • class로 생성되는 프로퍼티와 메소드는 인스턴스 레벨로, 인스턴스를 통해서만 접근하고, 호출할 수 있다.

인스턴스 ( instance )

어떠한 클래스를 바탕으로 만들어진 객체

class Car {
  constructor (brand, name, color) {
   this.brand = brand ;               
   this.name = name ;
   this.color = color ;
  }
  drive() {
    console.log(this.name + "가 운전을 시작합니다");
  }
}

//인스턴스 생성
const avante = new Car ('hyundai', 'avante', 'black') ;
console.log(avante);// Car { brand: 'hyundai', name: 'avante', color: 'black' }
avante.drive(); // avante가 운전을 시작합니다
  • 키워드 new에 정의한 클래스명을 입력하면 인스턴스가 생성된다.
  • 즉시 생성자 함수가 실행되며, 변수에 인스턴스가 할당 된다.
  • 생성된 인스턴스는 class의 고유한 속성과 메서드를 가진다.
  • class로 생성되는 프로퍼티와 메소드는 인스턴스 레벨로, 인스턴스를 통해서만 접근하고, 호출할 수 있다.

정적 메소드 ( static )

클래스의 정적 프로퍼티와 메소드를 정의할 때 static 키워드를 이용한다.

class Car {
  static carHorn = "bbang"; // 정적 프로퍼티

  constructor(brand, name, color) {
    this.brand = brand;
    this.name = name;
    this.color = color;
  }

  static makeCar = () => { // 정적 메소드
    return new Car ('kia', 'k5', 'white')
  }
  
  drive() {/Users/mjwoong/Desktop/드림코딩/직접 필기/6. 클래스/1.클래스기본.js
    console.log(this.name + "가 운전을 시작합니다");
  }
}
console.log(Car.carHorn); // bbang // 클래스를 이용하여 정적 프로퍼티 호출 
const k5 = Car.makeCar() // 클래스를 이용하여 정적 메소드 호출
console.log(k5); // Car { brand: 'kia', name: 'k5', color: 'white' }
  • 정적 프로퍼티와 메소드는 인스턴스로 호출이 불가능하다.
  • 정적 프로퍼티와 메소드는 클래스 이름으로 호출할 수 있다.
  • 정적 프로퍼티와 메소드는 클래스 레벨로, 클래스 레벨에서는 this를 사용할 수 없다.
  • 정적 메소드는 주로 Math객체의 메소드와 같이 전역에서 사용할 함수를 만들 때 사용되어진다.

클래스 필드

클래스에 있는 필드와 메소드는 기본적으로 외부에서 접근이 가능하다. => public 필드 및 메소드
접근제어자를 이용하면 필드와 메소드를 캡슐화하여 외부에서의 접근을 막을 수 있다. => private 필드 및 메소드

class Car {
  constructor(brand, name, color) {
    this.brand = brand;
    this.name = name;
    this.color = color;
  }

  drive = () => {
    console.log(this.name + "가 운전을 시작합니다");
  }  
}

const k5 = new Car ('kia', 'k5', 'white')
console.log(k5); 
// Car { drive: [Function: drive], brand: 'kia', name: 'k5', color: 'white' }

접근제어자를 이용하여 위에 출력된 인스턴스에서 color 필드와 drive 메소드를 외부에서 접근이 불가능하게 해보자.

class Car {
  #color; // 클래스 바디에 #color
  constructor(brand, name, color) {
    this.brand = brand;
    this.name = name;
    this.#color = color; // 사용하는 곳에도 #을 붙여줌.
  }

  #drive = () => {
    console.log(this.name + "가 운전을 시작합니다");
  }  
}

const k5 = new Car ('kia', 'k5', 'white')
console.log(k5); // Car { brand: 'kia', name: 'k5' } => 외부에서 color프로퍼티와 drive 메소드 접근x
---------------------------------------------------------------------------------------------
k5.#color = 'blue' // 외부에서 접근 불가능.
// => SyntaxError: Private field '#color' must be declared in an enclosing class

이처럼, 클래스 내부에서만 접근 및 사용을 하게하려면
해당 필드와 메소드 앞에 #을 붙여 외부에서 접근이 불가능하게 할 수 있다.


클래스 상속

다른 클래스의 필드와 메소드를 그대로 물려받는 것을 의미한다.
필드나 메소드가 겹치는 클래스들을 만들려고 할 때 유용하다. 부모 클래스로부터 상속받은 자식 클래스들은 공통된 부분을 같이 공유하고 각자 필요한 점을 추가 구현할 수 있다.
클래스 상속을 이용하면 코드를 재사용하여 중복되는 코드를 최소화 할 수 있다는 장점이 있다.

예시) 부모클래스로 사람을 나타내는 클래스를 만들고 회사원과 학생을 나타내는 클래스 각각를 만들어 상속을 구현해보자.
(3개의 코드는 모두 연속된 코드)

<부모 클래스 Person>

class Person {
  constructor (gender, age) {
    this.gender = gender;
    this.age = age;
  }
  eat () {
    console.log('밥먹자');
  }
}

<Person의 자식 클래스 OfficeWorker>

class OfficeWorker extends Person { // extends 키워드로 상속 관계가 정의됨.
  work () {                         // OfficeWorker 만의 메소드 추가
    console.log('일하자');
  }
}

const officeWorker = new OfficeWorker ('여자', '30');
console.log(officeWorker);// OfficeWorker { gender: '여자', age: '30' }
officeWorker.eat();// 밥먹자
officeWorker.work();// 일하자

<Person의 자식 클래스 Student>

class Student extends Person {
  constructor (gender, age, grade) {
    super(gender, age);              //super 키워드
    this.grade = grade;
  }
  eat () {                          // 오버라이딩(Overriding)
    console.log('급식실가서 밥먹자');
  }
  study () {
    console.log('공부하자');
  }
}

const student = new Student ('남자', '18', '2')
console.log(student); // Student { gender: '남자', age: '18', grade: '2' }
student.eat(); // 급식실가서 밥먹자
student.study(); // 공부하자

OfficeWorker와 Student 클래스는 Person 클래스에게서 필드와 메소드를 그대로 상속받았다. 그 이후 각자 필요한 것을 구현했음을 알 수 있다.
부모 클래스에서 상속받은 constructor에 추가할 것이 없다면 constructor 함수를 생략하면 된다.(OfficeWorker 참고)
그에 비해 부모 클래스에서 상속받은 constructor에 추가를 하려면 super키워드로 부모클래스의 constructor를 호출한 후에 추가하여 구현할 수 있다. (Student 클래스 참고)

  • extends 키워드로 부모 클래스에서 자식 클래스로의 상속이 이루어진다. 필드와 메소드를 그대로 물려받는다.

  • super 키워드는 부모 클래스를 참조하거나 부모 클래스의 constructor를 호출할 때 사용한다.

  • 오버라이딩 부모 클래스에게 상속받은 메소드를 자식 클래스에서 재정의하는 것을 말한다.

0개의 댓글