처음부터 차근차근

[코어 자바스크립트] Class 본문

Language/JavaScript

[코어 자바스크립트] Class

HangJu_95 2023. 6. 12. 21:32
728x90

다른 언어에서도 많이 쓰이는 Class란 무엇인가??

Class

Class : 객체 지향 프로그래밍(OOP)에서 특정 객체를 생성하기 위해 변수와 메소드를 정의하는 일종의 틀(template)이다.

(Wiki 백과)

 

간단히 설명하면, Class는 학교에서 다양한 종류의 책상을 만드는 설계도와 비슷하다.

Class로 만들어지는 Instance??

instance는 이 설계도를 보고 만들어지는 실제 책상이라고 생각할 수 있다.

 

결론 : 객체 지향 프로그래밍에서 Class는 객체를 찍어 내기 위한 틀이다.

그렇다면 Class를 쓰는 이유는??

빠르고, 정확하고, 다량으로 객체를 생성 할 수 있기 때문이다.

 

아래 예시를 보자.

 

// 클래스라는 설계도를 만들어봅시다.
class Person {
   // 우리는 사람이기 때문에 필수요소
   // name, age
   constructor (name, age) {
      this.name = name;
      this.age = age;
   }

   // 메서드 형태의 동사 표현
   // 인스턴트 자체는 this라고 했었음. >> this로 내부 값을 접근
   sayHello() {
      console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
   }

   sayAge() {
      console.log(`My Age is ${this.age} years old.`);
   }
}

// 설계도를 통해 인스턴스(실제 사물) 만들어봅시다!
// 이름은 홍길동이고, 나이는 30살인 사람 하나로 만들어줘!!!(설계도에 근거해서!)

// 왜 쓰냐?? 빠르고, 정확하게 많이 만들 수 있기 때문에.
const person1 = new Person("Alice", 30);
const person2 = new Person("Bob", 25);

// person1.sayHello();
// person2.sayHello();

 

위 코드에서 Person Class는 name과 age 속성을 가지고 있으며, sayHello 메소드를 정의한다. new 키워드를 사용하여 Preson Class의 인스턴스를 생성하고, sayhello 메소드를 호출한다.

 

Constructor

Constructor : Class의 생성자 함수. 생성자 함수는 객체를 생성할 때 호출되며, 객체를 초기화하는 역할을 한다.

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayHello() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
  }
}

const person = new Person('Alice', 20);

 

Getter와 Setter

Class에서는 getter와 setter를 사용하여 Class 속성에 접근할 수 있다. getter는 속성값을 반환하는 메소드이며, setter는 속성 값을 설정하는 메소드이다. 이를 통해 내가 생성한 Instance를 정해진 규격 안에서 자유자제로 변경 할 수 있다. 

// Getters와 Setters (프로퍼티에 접근하고 세팅하기 위한)
// 객체지향 프로그래밍 언어 -> G, S
// 클래스 --> 객체(인스턴스)
// 프로퍼티(constructor)
// new Class(a, b, c)

class Rectangle {
   constructor (height, width) {
      // underscore : private(은밀하고, 감춰야 할 때)
      this._height = height;  // this 앞에 _는 약속이다. 안그러면 무한 루프에 걸린다.
      this._width = width;
   }

   // width를 위한 getter
   get width(){
      return this._width;
   }

   // width를 위한 setter
   set width(value) {
      // 검증 1 : value가 음수이면 오류!
      if (value <= 0) {
        //
        console.log("[오류] 가로길이는 0보다 커야 합니다!");
        return;
      } else if (typeof value !== "number") {
        console.log("[오류] 가로길이로 입력된 값이 숫자타입이 아닙니다!");
        return;
      }
      this._width = value;
    }

   get height () {
      return this._height;
   }

   // 세터는 방법을 함수 형태로 쓸 수 있으며, 검증할 수 있다.(ex height가 음수인 경우)
   set height(value) {
      // 검증 1 : value가 음수이면 오류!
      if (value <= 0) {
        //
        console.log("[오류] 세로길이는 0보다 커야 합니다!");
        return;
      } else if (typeof value !== "number") {
        console.log("[오류] 세로길이로 입력된 값이 숫자타입이 아닙니다!");
        return;
      }
      this._height = value;
    }

    getArea() {
      const a = this._width * this._height;
      console.log(`넓이는 => ${a}입니다.`);
    }

}

const rect1 = new Rectangle(10, 7);
// const rect2 = new Rectangle(10, 30);
// const rect3 = new Rectangle(15, 20);

// rect1.getArea()

// getter 예시
console.log(rect1.width); // 10

// setter 예시
rect1.width = 20;
console.log(rect1.width); // 20

 

여기서 보면 this 뒤에, 모든 속성에 _(underscore)를 넣어주고 있다. 안그러면 무한 루프에 걸리기 때문이다.

 

왜 underscore를 넣어줘야 하는가??

아래 예시를 한번 보자.

class user{
    constructor(firstName,lastName,age){
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
    get age(){
        return this.age;
    }
    set age(value){
        this.age=value;
    }
}
const user1 = new user('steve','job',-1);
console.log(user1.age);

-1이 출력될 것 같은가? 아니다. Error가 출력된다.

https://boomrabbit.tistory.com/55

이 오류는 stack 메모리에서 무한정 쌓여서 더 이상 프로그램을 진행할 수 없다는 뜻이다. 왜일까??

 

getter와 setter의 특성 때문에 이러한 오류가 발생되는데, 

getter 메서드는 obj.propName을 사용해 프로퍼티를 읽으려고 할 때 실행되고, setter 메서드는 obj.propName = value으로 프로퍼티에 값을 할당하려 할 때 실행되기 때문이다.

 

먼저 user1 에 생성자를 할당해 주면서 동시에 초기화를 해주려고 한다. 그리고 constructor에서 this 라는 object를 통해 데이터에 접근한다. 이때 this 라는 object를 사용해 프로퍼티를 읽으려고 했으니 자동적으로 getter가 호출이 된다. 그리고 = age라고 프로퍼티에 값을 할당하려 했으니 setter가 자동적으로 호출이 된다.

근데 setter를 보면 this.age=value 라고 또다시 = value 라고 프로퍼티에 값을 할당하려 하니 setter가 다시 호출이 되는 것이다.


즉, setter의 호출이 무한정으로 반복되서 결국 stack메모리가 감당할 수 없게 되어 오류를 발생시키는 것이다.

 

이러한 오류를 해결하기 위해, getter와 setter에 사용되는 속성 이름을 바꾸는 것이고, 주로 underscore를 사용하여 바꾼다.

 

상속(Inheritance)

Class는 상속을 통해 다른 Class의 기능을 물려받을 수 있다. 삭속을 받는 Class는 subclass or derived class라고 하며, 상속을 하는 Class를 superclass 또는 base class라고 합니다.

// 상속
// Class -> 유산으로 내려주는 주요 기능!!
// 부모 <-> 자식

// 동물 전체에 대한 클래스

class Animal {
   // 생성자
   constructor(name) {
      this.name = name;
   }

   speak() {
      console.log(`${this.name} make a noise.`);
   }
}

class Dog extends Animal {
   // 부모에서 내려받은 메서드를 재 정의할 수 있음
   // = overriding...
   speak(){
      console.log(`${this.name} barks.`);
   }
}

const max = new Dog('Max');

max.speak()

위 코드에서 Dog Classs는 Animal Class를 상속 받았다. Dog Calss에서 speak 메소드를 overriding하여 Animal Class의 speak 메소드를 덮어쓴다.

 

다음 예제는 super를 사용한다.

자식 클래스에서 this를 이용하여 새로운 프로퍼티를 부여하거나, 상속받은 프로퍼티를 수정하고 싶다면 먼저 super()를 이용하여 부모 클래스의 기존 프로퍼티 값을 호출해야 한다.

 

또한 super는 객체의 부모가 가지고 있는 메서드를 호출하기 위해 사용된다.

 

// 새로운 Car class 정의
class Car {
   constructor(modelName, modelYear, type, price) {
     this.modelName = modelName;
     this.modelYear = modelYear;
     this.type = type;
     this.price = price;
   }
 
   // 클락션을 울리는 메서드
   makeNoise() {
     console.log(`${this.modelName}: 빵!`);
   }
   printModelYear() {
      console.log(this.modelYear + "년도 모델입니다.")
   }
}

// 전기차 Class 정의
class ElectronicCar extends Car {
   constructor(modelName, modelYear, price, chargeTime){
      // Car(부모 Class)에게도 알려주기!!
      // super => 부모의 constructor를 뜻함
      // 일렉트로닉카와 카의 싱크를 맞추기 위해 넣어준다.
      super(modelName, modelYear, "e", price)
      this._chargeTime = chargeTime
   }

   set chargeTime (value) {
      this._chargeTime = value
   }

   get chargeTime () {
      return this._chargeTime

   }
}
 
 // 자동차 만들기
//  const car1 = new Car("Sorento", "2023", "e", 5000);
//  const car2 = new Car("SM5", "1999", "g", 3000);
//  const car3 = new Car("QM6", "2010", "g", 4500);
//  car1.makeNoise();
//  car2.makeNoise();
//  car3.makeNoise();

const eleCar1 = new ElectronicCar("뉴아이오닉5", "2023", 7000, 60);

eleCar1.makeNoise()
eleCar1.printModelYear()
console.log(eleCar1.chargeTime);
eleCar1.chargeTime = 20;
console.log(eleCar1.chargeTime);

 

Static Method

static이라는 말에서 알 수 있듯이, 인스턴스를 만들지 않고 사용할 수 있다.

때문에 유틸리티 함수, 정적 속성인 경우 인스턴스 간에 복제할 필요가 없는 데이터(똑같은 것을 공유해서 쓸 때)를 만들 때 사용

class Calculator {
  static add(a, b) {
    return a + b;
  }

  static subtract(a, b) {
    return a - b;
  }
}

console.log(Calculator.add(1, 2)); // 3
console.log(Calculator.subtract(3, 2)); // 1