[코어자바스크립트] - 7장. 클래스

sypaik-dev·2023년 5월 26일
0
post-thumbnail

클래스와 인스턴스의 개념 이해

프로토타입 기반 언어

먼저, 자바스크립트는 프로토타입 기반의 언어이다. 여기서 프로토타입 기반 언어는 객체지향 패러다임의 한 형태이다.

객체지향 프로그래밍은 데이터와 그 데이터를 처리하는 동작을 하나의 단위로 묶어서 객체라는 개념으로 표현하는 방법론이다. 객체지향 언어는 클래스와 인스턴스를 기반으로 객체를 생성하고 관리하는데 중점을 둔다. 대표적인 객체지향 언어로는 Java, C++, C# 등이 있다.

반면 프로토타입 기반의 자바스크립트에서는 객체를 생성하기 위한 원본이 되는 prototype 객체를 기반으로 새로운 객체(Instance)를 생성한다. 이 새로운 객체는 프로토타입 객체의 속성과 동작을 상속받는다. 상속을 통해 객체는 프로토타입 객체의 메서드와 프로퍼티를 공유하게 된다.

다음은 클래스와 인스턴스의 개념을 이해하기 위한 그림이다.

Food와 Fruit는 모두 집단, 클래스이다. Food는 Fruit보다 상위의 개념으로 Fruit는 Food보다 하위의 개념으로 Food는 상위클래스(superClass)이고 Fruit는 하위클래스(subClass)이다. 클래스는 하위로 갈수록 상위 클래스의 속성을 상속하면서 더 구체적인 요건이 추가 또는 변경된다.
Fruit 클래스 안에 배, 포도, 딸기, 멜론, 포도는 Fruit의 인스턴스이다. 여기서 인스턴스란 어떤 클래스의 속성을 지니는 실존하는 개체를 일컫는다.

클래스와 인스턴스의 관계

현실세계에서 클래스와 인스턴스는 집합 개념으로 하나의 인스턴스가 여러 클래스에 속할 수 있지만 프로그래밍 언어에서는 정반대이다. 프로그래밍 언어에서는 하나의 인스턴스는 하나의 클래스만을 바탕으로 만들어진다. 클래스가 먼저 정의돼야만 공통적인 요소를 지니는 개체, 인스턴스들을 생성할 수 있다.

자바스크립트의 클래스

스태틱 메서드와 프로토타입 메서드

  • 스태틱 메서드 (Static Method)
    • 클래스에 직접 속한 메서드로 인스턴스에 상속되지 않는다.
    • 클래스 레벨에서 호출할 수 있으며, 클래스 이름을 통해 접근한다.
    • 주로 전역적인 기능을 제공하거나, 클래스와 관련된 유틸리티 메서드를 구현하는 데 사용된다.
  • 프로토타입 메서드 (Prototype Method)
    • 객체의 프로토타입에 속한 메서드이다.
    • 인스턴스에 상속되어 각 인스턴스에서 사용할 수 있다.
    • 프로토타입 체인을 통해 해당 메서드를 찾고 호출할 수 있다.

💡 인스턴스 메서드가 대신 프로토타입 메서드!
객체의 메서드는 해당 객체의 프로토타입 객체에 정의되어 있다. 객체의 프로토타입 체인을 통해 상위 객체의 메서드를 상속받아 사용한다. 따라서 '인스턴스 메서드'라는 용어보다 '프로토타입 메서드'가 프로토타입 기반의 객체 생성 방식을 반영하고 명확하게 설명할 수 있는 용어이다.

[예시 코드]

var Rectangle = function (width, height) {
	this.width = width;
  	this.height = height;
} // 생성자 

Rectangle.prototype.getArea = function () {
	return this.width * this.height;
} // 프로토타입 메서드 

Rectangle.isRectangle = function (instance) {
	return instance instanceof Rectangle &&
      instance.width > 0 && instance.height > 0;
} // 스태틱 메서드

var rect1 = new Rectangle(3, 4);
console.log(rect1.getArea());
console.log(rect1.isRectangle(rect1)); // 🚨Error
console.log(Rectangle.isRectangle(rect1));

해당 코드에서 두번째 출력값에서 에러가 발생하는데 이유가 무엇일까?
getArea는 프로토타입 메서드로 rect1 인스턴스가 접근할 수 있지만 isRectagle은 스태틱 메서드로 인스턴스가 직접 접근할 수 없다. 해당 스태틱 메서드는 클래스에 직접 속해 있어 인스턴스가 아닌 클래스 이름을 통해 호출되어야 한다.

💡 '클래스가 추상적일 수 있고 구체적인 개체가 될 수 있다'의 의미?
클래스가 추상적인 경우는 구체적인 객체를 생성하기 위한 '템플릿' 또는 '틀'의 역할을 담당하는 목적을 가질 때를 말한다.
반면 클래스가 구체적인 개체가 되는 경우는 클래스 자체를 this로 직접 접근해야만 하는 스태틱 메서드를 호출할 때의 클래스는 그 자체가 하나의 개체로서 취급된다.

상위 클래스에서의 접근 수단 제공

하위 클래스에서 상위 클래스의 프로토타입 메서드에 접근하기 위한 방법은 없을까? 상위 클래스의 메서드에 접근하고 호출하여 실행 결과를 사용하는 방식으로 super 키워드를 흉내낼 수 있다. 다음 예시를 보자.

const extendClass = function (SuperClass, SubClass, subMethods){
    SubClass.prototype = Object.create(SuperClass.prototype);
    SubClass.prototype.constructor = SubClass;
    SubClass.prototype.super = function (propName){
        const self = this;
        if (!propName) return function (){
            SuperClass.apply(self, arguments);
        };
        const prop = SuperClass.prototype[propName];
        if (typeof prop !== "function") return prop;
        return function () {
            return prop.apply(self, arguments);
        };
    }
    
    
    if (subMethods){
    	for (let method in subMethods){
            SubClass.prototype[method] = subMethods[method];
        }
    }
    Object.freeze(SubClass.prototype);
    return SubClass; 
}



const Rectangle = function (width, height) {
    this.width = width;
    this.height = height;
}

Rectangle.prototype.getArea = function () {
    return this.width * this.height;
}

const Square= extendClass(
    Rectangle,
    function (width){
        this.super()(width, width);
    }, {
        getArea: function (){
            console.log('size if :', this.super("getArea")());
        }
    }
);
const sq = new Square(10);
sq.getArea();     // size is : 100 (subclass의 메서드 실행)
console.log(sq.super("getArea")());   // 100  (superclass의 메서드 실행)

위의 코드에서 Rectangle이라는 상위 클래스와 Square라는 하위 클래스를 생성한다. Rectangle은 width와 height를 인자로 받는 생성자와 getArea 메서드를 가지고 있고 Square는 Rectangle을 상속받아 정사각형을 표현하는 클래스이다.

Square 클래스에서 getArea 메서드를 오버라이드하고 있다. 이 메서드에서는 먼저 하위 클래스의 메서드가 실행되고, 그 후에 this.super("getArea")()를 호출하여 상위 클래스의 getArea 메서드를 실행하고 있다. 이를 통해 상위 클래스의 실행 결과를 바탕으로 추가 작업을 수행하고 있다.

예를 들어, sq.getArea()를 호출하면 'size is: 100'이 출력되는데, 이는 하위 클래스인 Square의 getArea 메서드에서 추가 작업한 결과이다. 또한 sq.super("getArea")()를 호출하면 100이 반환되는데, 이는 상위 클래스인 Rectangle의 getArea 메서드를 실행한 결과이다.

이를 통해 위의 코드는 상위 클래스의 메서드에 접근하고 호출하여 실행 결과를 사용하는 방식으로 super 키워드를 흉내내고 있습니다.

ES6의 클래스 및 클래스 상속

ES5와 ES6 클래스 문법 비교

아래 코드는 ES5와 ES6의 클래스 문법을 비교하고 있다.

const ES5 = function (name) {
    this.name = name;
}
ES5.staticMethod = function () {
    return this.name + 'staticMethod';
}
ES5.prototype.method = function () {
    return this.name + 'method';
}

const ES6 = class {
    // 생성자
    constructor (name) {
        this.name = name;
    }
    
    // static method : 생성자 함수(클래스)만이 호출할 수 있음.
    static staticMethod () {
        return this.name + 'staticMethod';
    }
    
    // prototype method
    method () {
        return this.name + 'method';
    }
   
};

ES5는 ES5 버전에서 클래스를 생성하기 위해 사용되는 함수 생성자이다. ES5 함수 생성자에는 name이라는 인자를 받으며, this.name 프로퍼티를 초기화한다. ES5.staticMethod은 정적(static) 메서드로, 클래스 자체에 바로 접근하여 호출할 수 있다. ES5.prototype.method는 프로토타입 메서드로, 인스턴스를 통해 호출될 수 있으며, this.name + 'method'을 반환한다.

이와 달리, ES6은 ES6 버전에서 도입된 클래스 문법을 사용하여 클래스를 생성한다. ES6 클래스는 생성자 함수로 name을 인자로 받는다. 생성자 함수 내에서는 this.name을 초기화한다. staticMethod는 정적(static) 메서드로, 클래스 자체에 바로 접근하여 호출할 수 있다. 이 정적 메서드도 this.name + 'staticMethod'을 반환한다. method은 프로토타입 메서드로, 인스턴스를 통해 호출될 수 있으며, this.name + 'method'을 반환한다.

ES6 클래스 문법은 더 간결하고 직관적인 방식으로 클래스를 정의할 수 있게 해준다. 생성자 함수와 프로토타입 메서드를 따로 정의할 필요 없이 클래스 내에서 직접 정의할 수 있다. 또한, 정적 메서드를 static 키워드를 사용하여 간단하게 정의할 수 있다.

profile
Frontend Developer

0개의 댓글

관련 채용 정보