JavaScript

Class

차돌박이츄베릅 2023. 5. 28. 11:31

Class는 객체를 생성하기 위한 일종의 템플릿.

  • Class를 생성하기 위해서는 class 키워드를 사용
  • new 키워드를 사용하여 Class의 인스턴스를 생성
  • Constructor는 Class의 생성자 함수입니다. 
    생성자 함수는 객체를 생성할 때 호출되며, 객체를 초기화하는 역할을 합니다. 
    constructor 키워드를 사용하여 정의합니다.
  • constructor 메소드는 name과 age를 인자로 받아 this.name과 this.age 속성을 초기화합니다.
 
class Car {
    // 모든 클래스는 생성자함수가 있어야 함
    // 생성자함수답게 함수 모양을 갖춰야 됨
    // 필수값 넣기 : 필수 요소들을 반드시 넣어줘야됨. constructor(){} 생성자에게 이것을 넣어줌. 매개변수 자리에 필수로 넣어야되는걸 넣어주면 됨
    constructor(modelName, modelYear, type, price) {
        this.modelName = modelName; // 왼쪽 this는 우리가 만들 인스턴스(실체)를 가리킴, 오른쪽 modelName은 외부로부터 받아온거
        this.modelYear = modelYear;
        this.type = type;
        this.price = price;
    }

    // name, age처럼 단순한 이름의 명사만 있는게 아니라 동사도 있음
    // 메서드 형태의 동사 표현
    // 생성자가 만들어질 때 같이 만들어지는거

    // 클락션을 울리는 메서드
    makeNoise() {
        // 인스턴스의 modelName을 가져와야하기때문에 this.modelName로 씀
        console.log(`${this.modelName} 뛰뛰빵빵?`);
    }

    // 몇년도 모델인지 메서드
    printModelYear() {
        console.log(`${this.modelName}${this.modelYear}년도의 모델입니다.`);
    }
}

// 설계도를 통해 인스턴스를(실제 사물) 만들어봅시다!

// 자동차 만들기
// 한 번 만들어서 변경시키거나 하는게 아니기때문에 상수 const로 만들어봤음
// new 키워드를 이용해서 새로운 인스턴스를 만든다
const car1 = new Car('르노', '9180', '승용차', 5000);
const car2 = new Car('현대', '1990', '스포츠카', '3천만원');
const car3 = new Car('기아', '2023', 'SUV', '4천만원');

car1.makeNoise();
car1.printModelYear();
car2.makeNoise();
car2.printModelYear();
car3.makeNoise();
car3.printModelYear();

 

 


Getters와 Setters

Class에서는 getter와 setter를 사용하여 Class의 속성에 접근할 수 있습니다.

  • getter: 속성 값을 반환하는 메소드
  • setter: 속성 값을 설정하는 메소드

이를 통해 생성한 인스턴스를 정해진 규격 안에서 자유자제로 변경할 수 있습니다.

 

getters랑 setters를 쓸 때는 변수 이름을 똑같이 쓰게 되면 무한루프에 빠지는 이슈때문에

this에 접근하는 property의 이름은 항상 앞에 언더스코어_를 붙여줌

// Getters와 Setters
// 객체지향 프로그래밍 언어에서는 G, S
// 클래스를 통해서 객체(인스턴스)를 만드는데 그 안에는 프로퍼티가 있음(constructor에서 정의)
// 그 프로퍼티에 접근하기 위한 Getters와 Setters를 제공. 안전하고 간편하게 값을 가져올 수 있음

class Car {
    constructor(modelName, modelYear, type, price) {
        // (1) modelName에 들어온 값을 this.modelName에 set하기 위해서 set modelName(value){~}로 들어감
        // (4) getters랑 setters를 쓸 때는 this에 접근하는 property의 이름은 항상 앞에 언더스코어_를 붙여주기로 약속!
        // underscore : private(은밀하고, 감춰야 할 때) 인스턴스 내에서만 쓰이기 위한 변수로서 이제 분리해놓겠다는 말임. getters랑 setters에서 변수이름을 똑같이 써버리면 겹치게 되는 현상이 생기게되면서 언더스코어로 분리시켜야되는 필요성이 생김
        this._modelName = modelName;
        this._modelYear = modelYear;
        this._type = type;
        this._price = price;
    }
   
    // modelName를 위한 getter
    // get하려면 내부의 값 리턴
    get modelName() {
        return this._modelName;
    }

    // modelName를 위한 setter
    // set하는 방법을 함수 형태로 쓸 수 있음. 안에서 검증 가능. set할 수 없는 값 등을 검증
    // setter를 쓰게 되면 외부로부터 값이 들어왔을 때 내부적으로 값을 검증한 후에 세팅을 할 수 있음
    set modelName(value) {
        // 안으로 어떤 value를 set해줄거냐
       
        // 검증 : 유효성 검사
        if (typeof length <= 0) {
            console.log('[오류] 모델명이 입력되지 않았습니다. 확인해주세요!');
            return;
        } else if (typeof value !== 'string') {
            console.log('[오류] 입력된 모델명이 문자형이 아닙니다!');
            return;
        }

        this._modelName = value;
        // (2) this.modelName에 value로 넘겼던 '르노'라는 값을 다시 세팅함
        // (3) 그런데 this.modelName에 set하기 위해서는 다시 set modelName(value) {...}으로 감. 계속 가. 무한루프..
    }

    // modelYear를 위한 getter
    get modelYear() {
        return this._modelYear;
    }

    // modelYear를 위한 getter
    set modelYear(value) {
        if (typeof length !== 4) {
            console.log('[오류] 입력된 년도가 4자리가 아닙니다. 확인해주세요!');
            return;
        } else if (typeof value !== 'string') {
            console.log('[오류] 입력된 모델명이 문자형이 아닙니다!');
            return;
        }

        this._modelYear = value;
    }

    get type() {
        return this._type;
    }

    set type(value) {
        if (typeof length <= 0) {
            console.log('[오류] 타입이 입력되지 않았습니다. 확인해주세요!');
            return;
        } else if (value !== 'g' && value !== 'd' && value !== 'e') {
            console.log('[오류] 입력된 타입이 잘못되었습니다. 확인해주세요!');
            return;
        }

        this._type = value;
    }

    get price() {
        return this._price;
    }

    set price(value) {
        if (typeof value !== 'number') {
            console.log('[오류] 가격으로 입력된 값이 숫자가 아닙니다. 확인해주세요!');
            return;
        } else if (value < '1000000') {
            console.log('[오류] 가격은 100만원보다 작을 수 없습니다. 확인해주세요!');
            return;
        }

        this._price = value;
    }

    // 클락션을 울리는 메서드
    makeNoise() {
        console.log(`${this._modelName} 뛰뛰빵빵?`);
    }

    // 몇년도 모델인지 메서드
    printModelYear() {
        console.log(`${this._modelName}${this._modelYear}년도의 모델입니다.`);
    }
}

// 자동차 만들기
const car1 = new Car('르노', '9180', '승용차', 5000);
const car2 = new Car('현대', '1990', '스포츠카', '3천만원');
const car3 = new Car('기아', '2023', 'SUV', '4천만원');

// car1.makeNoise();
// car1.printModelYear();
// car2.makeNoise();
// car2.printModelYear();
// car3.makeNoise();
// car3.printModelYear();

// getter 예시1
console.log(car1.modelName);
// setter 예시1
car1.modelName = 'kia';
console.log(car1.modelName);
car1.modelName = 1;
console.log(car1.modelName);

 


상속(Inheritance)

Class는 상속을 통해 다른 Class의 기능을 물려받을 수 있다.

  • 상속을 받는 Class : subclass 또는 derived class
  • 상속을 하는 Class : superclass 또는 base class
// 상속(Inheritance)
// Class에 기능을 유산으로 내려주는 주요 기능!!
// 부모 <-> 자식 간의 관계. 그래서 클래스 독단적으로 존재하는게 아니라 부모클래스, 자식클래스, 손자클래스가 존재할 수 있음. 부모가 가지고 있는 형질인 것들을 밑으로 상속해서 내려주는게 효과적

// 자동차 전체에 대한 클래스
class Car {
    // 생성자 함수 (필수로 들어가야하는거) {}
    constructor(modelName, modelYear, type, price) {
        this.modelName = modelName; // getters, setters 안쓸거라 그냥 this.modelName으로 썼음
        this.modelYear = modelYear;
        this.type = type;
        this.price = price;
    }

    // 클락션을 울리는 메서드
    makeNoise() {
        console.log(`${this.modelName} 뛰뛰빵빵?`);
    }

    // 몇년도 모델인지 메서드
    printModelYear() {
        console.log(`${this.modelName}${this.modelYear}년도의 모델입니다.`);
    }
}

// ElectronicCar라는 class를 만들 때 옆에 익스텐즈extends라는 키워드를 써 줌!
// 익스텐즈에서 어떤 클래스의 상속을 받을거냐를 오른쪽에 씀
// Car의 내용을 기본적으로 끌고오기 때문에 constructor을 써줄 필요가 없음
// makeNoise()메서드도 기본적으로 들어가는데 재정의할 수도 있음
// 전기차 Class 정의
class ElectronicCar extends Car {
    // 만약 재정의가 필요하다면 constructor을 써줘야됨. 우리는 type이 필요없어서 재정의하겠음
    constructor(modelName, modelYear, price, chargeTime) {
        // Car(부모 class)에게도 알려주기!!
        // super키워드는 부모의 constructor임. 그래서 그대로 가져오는데 고정인것만 고정값 넣어줌
        // 부모와 자식의 constructor가 다르기 때문에 그 싱크를 맞춰주기 위해서 super를 쓴다
        super(modelName, modelYear, 'e', price); // 이건 내부 속성(부모 class)이니까 여긴 chargeTime안씀
        this._chargeTime = chargeTime;
    }

    // 부모에게서 내려받은 메서드를 재정의 할 수 있음
    // overriding
    makeNoise() {
        console.log(`${this.modelName} 뒤뒤방방..`);
    }

    set chargeTime(value) {
        this._chargeTime = value;
    }
    get chargeTime() {
        return this._chargeTime;
    }
}

// 부모 클래스가 Car이기 때문에 안에 설정해놓은 name을 무조건 받아야 함 or 재정의한 이름
const eleCar1 = new ElectronicCar('테슬라', '2023', 9000, 60);
eleCar1.makeNoise();
eleCar1.printModelYear();
console.log('--------------------');
console.log(eleCar1._chargeTime);
eleCar1.chargeTime = 20;
console.log(eleCar1._chargeTime);

 

 


Static Method

Class에서는 static 키워드를 사용하여 Class 레벨의 메소드를 정의할 수 있습니다. Class 레벨의 메소드는 인스턴스에서 호출할 수 없으며, Class 이름으로 직접 호출할 수 있어요. 

static이라는 말에서 알 수 있듯이, 인스턴스를 만들지 않고 사용할 수 있기 때문에 유틸리티 함수, 정적 속성인 경우 인스턴스 간에 복제할 필요가 없는 데이터(똑같은 것을 공유해서 쓸 때)를 만들 때 사용되곤 합니다!! 

즉, 인스턴스를 만들 필요가 없을 때 사용한다고 이해하시면 편해요

// Static Method 정적메서드
// Class는 객체를 만들기 위해서 사용
// 다량으로, 안전하고, 정확하게 만들기 위해 사용

// 굳이 인스턴스화 시킬 필요가 없는, 즉 객체를 만들지 않아도 되는, 변하지 않는 정적인 메서드들은 Static Method로 정의하고 클래스 자체를 호출하는 식으로 사용하는 방법들이 있음
// 클래스 자체는 일반 클래스인데, 그 안에 들어있는 메서드가 이제 static 정적인 특징을 갖는다.
// 원래는 인스턴스화 시킨다음에 인스턴스.메서드로 접근해야 했다면 Static Method는 아래 참고

class Calculator {
    // constructor 필요없고
    // add라는 메서드 앞에 static이라는 키워드를 써주게 되면 Calculator.add(3, 5);로 접근 가능
    static add(a, b) {
        console.log('[계산기] 더하기를 진행합니다.');
        return a + b;
    }

    static substract(a, b) {
        console.log('[계산기] 빼기를 진행합니다.');
        return a - b;
    }
}

// 인스턴스화 시키지 않았지만 static 키워드를 통해서 add라는 메서드를 바로 호출할 수 있었다
Calculator.add(3, 5);
Calculator.substract(3, 5);

// 디자인 패턴에 의해서 상당히 많은 부분에 적용할 수 있음 !
// 객체지향의 장점! 객체 단위로 묶어서 여러 군데서 재활용할 수 있다. 효율성을 가져다 줄 수 있다

 

 

 

 

'JavaScript' 카테고리의 다른 글

숙제1 - 문자열 연습하기  (0) 2023.05.28
클로저  (0) 2023.05.28
비동기 작업의 동기적 표현 - Promise, Generator, Async/await  (1) 2023.05.28
콜백 함수  (0) 2023.05.28
this 바인딩 - call, apply, bind  (0) 2023.05.25