본문 바로가기

frontend/javascript

[JavaScript] 04 Class

이번 게시글에서는 클래스에 대해 다뤄볼 것이다. 이미 JAVA를 배우면서 어느정도 익혔기 때문에, 너무 세세하게 다루진 않겠다.

 

Class - Template
Object - instance of a class

1. Class Declarations

 

class Person {
    // constructor
    constructor(name, age) {
        // fields
        this.name = name;
        this.age = age;
    }
    
    // methods
    speak() {
        console.log(`${this.name}: hello!`);
    }
}

const min = new Person('min', 22);
console.log(min.name); // min
console.log(min.age); // 22
min.speak(); // min: hello!

 

 JS 에서 생성자 메소드는 constructor() 를 통해 구현한다. 참고로 생성자 메소드는 해당 클래스의 객체를 생성할 때, 해당 객체의 필드 변수에 할당할 값을 전달하여 대입(할당)하는 역할을 한다.

위 코드를 통해 Person 클래스는 필드변수로 name, age를 가지고 있으며, 메소드로는 speak() 메소드를 가지고 있음을 알 수 있다.

 

 

 

2. Getter and Setter

 

JS 에서 Getter와 Setter 메소드는 각각 get, set 키워드로 구현한다. 참고로 Getter는 클래스 내의 필드변수의 값을 반환하는 메소드이며, Setter는 클래스 내의 필드변수 값을 수정하는 역할을 한다. Getter 메소드를 통해 private 속성의 필드변수 값을 읽을 수 있다. 그리고 객체의 필드변수에 바로 접근하여 잘못된 값으로 수정할 경우를 대비해 별도의 Setter 메소드를 구현하는 것이다. 메소드 내부적으로 잘못된 값이 들어오는 경우에 예외처리를 할 수 있기 때문이다. 

 

class User {
    constructor(firstName, lastName, age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
    
    // getter
    get age() {
        return this.age;
    }
    
    // setter
    set age(value) {
        this.age = value < 0 ? 0 : value;
    }
}

const user1 = new User('Steve', 'Job', -1);

 

주의할 점

 

하지만, 이대로 코드를 작성하여 실행하면 stack overflow 에러가 발생한다. 원인은 getter와 setter 메소드를 생성하는 순간 생기는 특징(?)에 있다. 

 

우선 getter 메소드를 알아보도록 하겠다. JS에서 클래스 내부에 getter 메소드를 구현하게 되면, 해당 필드변수를 호출하는 구문( 여기서는 this.age 에 해당 )에서는 그 필드변수의 값을 직접 불러오는 것이 아니라, getter 메소드를 실행하게 된다. 

마찬가지로 JS에서 클래스 내부에 setter 메소드를 구현하게 되면, 해당 필드변수의 값을 할당하는 구문( 즉, assignment( = )를 포함하는 문장 )을 만날 때, 해당 필드변수에 바로 값을 할당하는 것이 아니라 setter 메소드를 실행하게 된다. 

 

이 내용을 종합해보면, 생성자 메소드의 this.age = age; 에서 문제가 발생한다는 점을 유추할 수 있다.

 

this.age = age;

앞에서 설명했듯 JS 에서는 이 구문에서 assignment( = )를 만났기 때문에, 필드변수의 값을 자체적으로 직접 다루는 것이 아니라 setter 메소드를 호출하게 된다.

 

set age(value) {
    this.age = value < 0 ? 0 : value;
}

 하지만 이 setter 메소드 내부에서도 assignment( = )가 실행되고 있는 것을 볼 수 있다. 그렇기 때문에 다시 또 setter 메소드를 호출하게 되고, 또 setter 메소드 내부에 assignment( = )가 실행되면서 다시 setter 메소드를 호출한다. 이러한 반복 과정을 통해 무한루프에 빠지게 되는 것이다. ( 함수를 호출하면 stack에 함수를 쌓아놓게 되는데, 여기서는 각 함수의 실행이 끝나지 않고 무한대로 생성되고 있기 때문에 overflow가 발생하는 것이다. )

그렇기 때문에 다음과 같이 코드를 수정해야 overflow를 막을 수 있다. 

 

class User {
    constructor(firstName, lastName, age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
    
    // getter
    get age() {
        return this._age;
    }
    
    // setter
    set age(value) {
        this._age = value < 0 ? 0 : value;
    }
}

이렇게 수정하게 되면, 생성자 메소드 내부에 있는

 

this.age = age;

이 문장은 age 라는 setter 메소드를 호출하게 된다. 

 

set age(value) {
    this._age = value < 0 ? 0 : value;
}

이 setter 메소드에서는 _age 라는 필드변수를 생성하여 값을 할당하고 있는 것을 볼 수 있다.

그러므로 이 User 클래스에는 firstName, lastName, _age 라는 총 세 개의 필드변수가 존재하는 것이다. 생성자 메소드 내부에서 사용한 age 라는 변수는 필드변수가 아니라 getter와 setter를 호출하기 위해 사용되는, 도구로서의 변수로 생각하면 편할 것이다. 

 

 

 

3. Fields ( public, private )

 

private 필드 변수는 변수명 앞에 # 기호를 붙이면 된다. 이 private 변수는 클래스 외부에서 접근할 수 없기 때문에, getter 메소드로만 값을 읽어올 수 있다. 

class Experiment {
    publicField = 2;
    #privateField = 0;
}

const experiment = new Experiment();

 

 

 

4. Static properties and methods

 

class Article {
    static publisher = 'minsan';
    constructor(articleNumber) {
        this.articleNumber = articleNumber;
    }
    
    static printPublisher() {
        console.log(Article.publisher);
    }
}

const article1 = Article(1);
const article2 = Article(2);
console.log(article1.printPublisher()); // minsan
console.log(article2.printPublisher()); // minsan

 

static 변수는 해당 클래스를 상속받은 모든 객체들이 공통적으로 공유하는 값이다. 

 

 

 

5. Inheritance (상속) - a way for one class to extend another class.

 

상속 개념은 간단히 말해서 부모의 속성을 자식이 그대로 전달받는 것을 말한다. 공통적으로 공유하고 있는 속성을 부모 클래스에 지정하고, 서로 다른 부분은 각 자식 클래스에서 오버라이딩하거나 별도의 필드변수 혹은 메소드를 추가하면 되는 것이다. 이때 오버라이딩이란 부모 클래스에 명시한 메소드를, 자식 클래스에서 해당 클래스의 특성에 맞게 수정하는 것을 말한다. 이러한 상속을 활용하면 코드의 중복을 줄일 수 있어 매우 유용하다. 

class Shape {
    constructor(width, height, color) {
        this.width = width;
        this.height = height;
        this.color = color;
    }
    
    draw() {
        console.log(`drawing ${this.color} color of`);
    }
    
    getArea() {
        return this.width * this.height;
    }
}

class Rectangle extends Shape{}
class Triangle extends Shape{
    // overriding
    draw() {
        super.draw();
        console.log('▲');
    }
    
    getArea() {
        return (this.width * this.height) / 2;
    }
    
    toString() {
        return `Triangle: color: ${this.color}`;
    }
}

 

 

'frontend > javascript' 카테고리의 다른 글

[JavaScript] 06 Array  (1) 2021.09.15
[JavaScript] 05 Objects  (0) 2021.09.02
[JavaScript] 03 Operators  (0) 2021.08.26
[JavaScript] 02 Function  (0) 2021.08.26
[JavaScript] 01 Variable  (0) 2021.08.25