it공부 (개념)/javascript

prototype(프로토타입)과 객체지향 프로그래밍: Typescript Decorator를 위한 Javascript(자바스크립트) 사전지식

cantor 2023. 1. 29. 21:08

 

Prototype?(프로토타입)의 의미

    • 사전적 의미:
      proto: 원래의, 원시적인 + type: 형태, 유형, 카테고리

      prototype: 원래의 형태, 원형, 임시 모델

 

      • Prototype in javascript(자바스크립트, 이하 JS):


        객체의 특징, 행동에 대한 정보를 포함하고 있는 Object(오브젝트)이다. 
        class를 이용하여 객체지향을 구현하는 다른 프로그래밍 언어와는 차별화되는 JS만의 독특한 요소이다.

        원시타입(number, string, boolean, undefined, null)을 제외한 모든 JS의 요소들은 프로토타입을 가지고 있다.
        array(배열), object(오브젝트), fuction(함수) 같은 컴포넌트들은 이미 정의되어 있는 프로토타입을 청사진으로 삼아 생성된다. 

        내가 정의한 객체가 특정속성을 직접 갖고 있지 않아도,
        그 객체의 프로토타입에 특정속성이 이미 존재한다면 자유롭게 사용할 수 있다.

 

    • 미리 정의된 프로토타입  예시: object와 hasOwnProperty 메서드
let exObject1 = { a: 0, b: 1};
let exObject2 = { c: 2, d: 3};

위의 코드를 구글크롬 개발자 도구의 콘솔에 입력해 보자.

(크롬에서 마우스 우클릭 ->검사(inspect)를 누르면 나오는 창에서 console란에 입력하면 된다.)


 hasOwnProperty property(속성)를 이용하여 두 오브젝트의 성분검사를 수행해 보자.

exObject1.hasOwnProperty('a');
exObject2.hasOwnProperty('b');

 

console 출력

 

 

위의 코드에선 "hasOwnProperty" 속성을 따로 정의해주지 않았다.

 

하지만 이 속성을 exObject1, 2에 사용할 수 있는 이유는 

오브젝트들이  "hasOwnProperty"가 들어있는 기본 오브젝트의 프로토타입을 상속받기 때문이다.

 

 

 

"__proto__속성": 객체의 프로토타입을 가져오는 속성

 

 

다시 개발자 콘솔에 다음과 같은 코드를 입력해 보자.

exObject1.__proto__

 

hasOwnProperty가 정의되어있는 모습

exObject1의 prototype에 __proto__를 포함한 여러 특성이 정의되어 있는 것을 알 수 있다.

 

 

미리 정의되어 있는 프로토타입

 

다음은 MDN문서의 array, function, object의 기능 속성 목차이다.

 

. prototype을 보면 알 수 있듯이  JS에서 기본적으로 사용하는 타입메서드들은 전부

각 객체별 prototype 오브젝트 안에 미리 정의되어 있다.

 

 

 

    • 생성되는 프로토타입:
      cunstructor (생성함수, 생성자) 객체를 정의하면, 
      그 객체에 의해 생성될 instance(인스턴스)의 프로토타입이 자동으로 생성된다.

예제코드: class와 function으로 정의한 생성자

class Pizza {
             constructor(name, price){
                 this.name = name;
                 this.price = price;
             }
            };

function createPizza (name, price) {
    this.name = name;
    this.price = price;
};

위와 같은 코드를 콘솔에 입력해 보자.

 

 

Pizza와 createPizza 생성자가 선언될 때, 각 객체의 instance(인스턴스)를 위한 프로토타입도 생성된다.

 

". prototype" 속성 생성자와 함께 생성된 프로토타입을 가져온다.

.prototype

 

 

 

인스턴스는 prototype 속성을 사용할 수 없다.

 

예제코드: 인스턴스 생성

pizza1 = new Pizza("peperoni", 10,000 won)
pizza2 = new createPizza("pineapple", 30,000 won)

 

인스턴스는 prototype키워드는 사용할 수 없다.

 

 

 "prototype"속성은 대상이 되는  생성자객체로 생성될 인스턴스의 프로토타입을 반환하는 키워드이다.

 

pizza1과 pizza2는 생성자를 통해 생성된 인스턴스이다. 

즉 생성자는 아니기 때문에 prototype 키워드를 사용할 수 없다.

 

 

반면에 "__proto__" 속성은 생성자들도 사용할 수 있다.(Pizza 클래스와 createPizza 함수)

생성자들의 프로토타입: 함수

 

 

요약: __proto__와 prototype의 차이

 

__proto__ 속성은 모든 객체가 사용할 수 있다.

prototype 속성은 생성자만 사용할 수 있다.

 

 

 

프로토타입에 속성 추가하기

 

prototype 속성을 통해 새로운 속성을 추가할 수 있다.

// num %만큼 할인이라는 문장을 반환하는 속성

Pizza.prototype.service = function (num) {
                 return "congratuation " + num + "%  discount";
                 };

 

pizza1에 새로운 속성을 추가하고, 사용하는 모습. 당연히 createPizza에 의해 생성된 pizza2에는 service가 없다.

 

 

이렇게 JS는 객체의 특징과 행동을 프로토타입으로 묶는

독특한 방식으로 객체지향 프로그래밍을 구현하고 있다.

 

 

 

  • etc +2015년에 발표된 ES6 JS부터는 "Class" 문법이 도입되었다.
    그래서 JS도 파이썬이나 여타 언어와 같은 방식을 이용해 객체지향을 구현할 수 있게 되었다.
    하지만 프로토타입을 사용하는 것이 JS 객체지향의 원형이다.

 

 

객체지향(object oriented)?

객체지향 프로그래밍은 프로그램을 객체의 집합으로 생각하겠다는 프로그래밍 철학이다.

이전의 프로그램들은 procedual oriented(절차지향)이라는 철학을 가지고 있었다.

 

프로그래머들은 보통 코드의 동작순서에 맞게 top-down (위-> 아래)의 방향에 순서를 맞춰 프로그래밍을 한다.

이렇게 프로그램을 "순서규칙"이 중요한 함수 및 프로시저의 집합으로 생각하는 것이 절차지향 프로그래밍이다.

 

객체지향의 등장

데이터규모가 커지고 프로그램이 복잡해짐에 따라
특정 데이터정보와 그 데이터를 조작하는 함수들을 묶어 관리할 필요성이 점점 크게 요구되었다

이러한 배경을 바탕으로 데이터를 property(특성), 데이터의 조작함수를 behavior(행동)으로 묶어
하나의 "객체"로 표현하는 객체지향 프로그래밍이 등장하였다.

 

절차지향과 객체지향은 서로 상반되는 개념이 아니다.

우리는 지금도 여전히 절차지향으로 프로그래밍하고 있다.



다만 개발할 때 중점적으로 사고하는 방식, 즉 개발의 중심 철학이 바뀐 것이다.

 

"어떤 순서로 코드가 실행되게 할까?"를 중점으로 사고하지 않고,
"어떤 객체들을 만들어야 할까?"를 중점으로 사고한다면 그것이 객체지향 프로그래밍이다.

 

 

객체지향의 4가지 특징

    • Abstraction(추상화): 
      복잡한 실제 행동이나 특징을 생략하고 유저에게 꼭 필요한 정보만 제공하는 것을 의미한다.

      ex)
      cook (조리) 특성을 가지고 있는 food라는 class와
      food를 상속받는 두 개의 클래스 Friedfood(튀긴 요리)와 Boiledfood(삶는 요리)를 생각해 보자.

      Friedfood의 cook은 튀기기 기능, Boiledfood의 cook은 삶기 기능이다.

      subclass의 cook특성은 이름만 가질 뿐 전혀 다른 행동이므로
      food에 cook의 세부적인 동작이 정의되어 있으면 안 된다.


    • Inheritance(상속):
      어떤 새로운 요소를 정의할 때, 이미 존재하는 요소의 특징과 행동을 물려받는 것을 뜻한다.
      똑같은 코드를 여러 번 반복정의할 필요 없이, 상위개체에 한번 정의된 것을 가져다 쓰므로
       코드의 재사용성이 매우 좋아진다.

 

    • Encapsulation(캡슐화):  
      외부로부터의 데이터 접근을 막고, 필요한 부분만 보이게 하는것을 뜻한다.
      주로 private 등의 접근제어자로 구현한다. 코드의 안정성이 좋아진다.

 

  • polymorphism(다형성):  
    같은 이름을 사용하지만 다른 특성이나 행동을 보유할 수 있는 것을 말한다.
    ex) 

    "hi"+ "hi" === "hihi"
    6 + 7 === 13

    "+" 연산자는 피연산자의 타입에 따라 전혀 다른 기능을 수행하고 있다.

 

타입스크립트 예제코드로 객체지향의 4가지 특징 살펴보기

//다음은 햄버거 가게를 차리기위한 타입스크립트 코드이다.



interface Hamburger {
    name: string;
    price: number;

    recheck(phrase: string): void;
}
//추상화
// 꼭 필요한 타입의 정보와 행동의 종류(이름)만 구현해 놓았다.
//  Hamburger를 상속받는 객체들이 필요한 본질만 정의해놓은 것이다.



class HamburgerClass implements Hamburger {
    name: string;
    price: number;
    private secretRecipe: string;
    
    // 캡슐화
    // 손님별 맞춤 secretRecipe를 private으로 선언함으로써 
    // HamburgerClass.secretRecipe를 통한 접근을 차단했다.

    constructor(name: string, price: number, secretRecipe: string) {
        this.name = name;
        this.price = price;
        this.secretRecipe = secretRecipe;
    }

    recheck(phrase: string) {
        console.log(phrase + ' check your order, ' + this.name  + ' is your choice?');
        
    }

}



class EventHamburgerClass extends HamburgerClass {

    // 상속
    // EventHamburgerClass는 HamburgerClass의 속성을 전부 가져왔다.
    
    
    sayEvent(phrase: string) {
        console.log(phrase + ' congraturations! '
        + this.name + ' with Event !!' +'select the toy which you want' );
    }
     recheck(phrase: string) {
        console.log(phrase + 'total price ' + this.name + '  is '  + this.price + ' won');
    }
    
    // 다형성 (함수오버로딩)
    // recheck를 새로 정의해줌으로써 HamburgerClass와 EventHamburgerClass
    // 는 서로 다른 기능을 하는 recheck 속성을 갖게되었다.

}




let orderFirst = new HamburgerClass('chicken burger', 5000, "spicy sauce")
orderFirst.recheck('sir, ')

let orderSecond = new EventHamburgerClass('beef burger', 7000, "sweet sugar")
orderSecond.recheck('excuse me')

//
//recheck.orderFirst에서는 햄버거의 이름을
//recheck.orderSecond에서는 가격을 출력한다

console.log("use Orderplus function")
console.log("OrderPlus(x: any string for names or any number for price, orderFirst, OrderSecond )")


function OrderPlus(x: string | number, od1: HamburgerClass, od2: HamburgerClass) {
    if (typeof x === 'string') {console.log(od1.name + ' and ' +  od2.name);}
    else if(typeof x === 'number' ) {console.log(od1.price + od2.price)}
}

// 하나의 함수로 표현한 다형성

// x에 문자를 대입했을경우 각 오더별 햄버거의 이름들을 출력한다.
// x에 숫자를 대입할시, 각 오더별 햄버거 가격의 합을 출력한다.
//  OrderPlus라는 하나의 이름하에 타입별로 다른 기능이 구현되어있다.


OrderPlus('name', orderFirst, orderSecond);
OrderPlus(7, orderFirst, orderSecond);

orderSecond.sayEvent('wait a minute');


//캡슐화
//orderFisrt.secretRecipe
//오류 Property 'secretRecipe' is private and only accessible within class 'HamburgerClass'

 

 

썸네일 이미지 출처: Model icons created by Freepik - Flaticon



부족한 글 읽어주셔서 감사합니다 혹여 궁금하신 점이나 오탈자, 정정내용이 있을 경우 댓글로 알려주세요.