대망의 프로토타입답게 설명이 길어질거 같군요.

사실상 프로토타입이 자바스크립트 문법의 꽃이라고 보면 됩니다.

이거 덕분에 자바스크립트가 오늘날 아주 화려해진거고요.

프로그래밍 익숙하지 않으신 분들은 장담컨데 토나온다고 하실겁니다.

(저도 prototype 구현으로 작성된 자바스크립트 소스 처음볼때 미치는줄 알았습니다ㅠㅠ 그땐 누가 뭐 알려주지도 않고 옆에서 갈구기만 하고 아주 그냥 XX XXX XXXX XXXXXXXXXXXXXXX!!!!!!!)

그래서 프로토타입은 2탄 내지 3탄 정도로 나눠서 정리하겠습니다. 만약 다 보고도 이해가 안되시면 프로그래밍 경험을 더 쌓고 오세요. 제가봐도 너무 4가지 없지만 이게 현실입니다. 회사가면 자비 따위 없습니다...

그럼 시작하겠습니다.


※ constructor

자바스크립트에는 클래스라는 구조는 없지만, 객체의 구조를 미리 만들어서 공유하거나 공통된 프로퍼티를 지정하기 위해 생성자 함수를 선언할 수 있습니다. 그리고 new 키워드를 통해 생성자에서 구현한대로 틀을 갖춘 객체가 생성되어 반환되죠.

예제를 통해 설명하겠습니다.

function dateExprObj(_year, _month, _date) {
  this.checkParams = function(__year, __month, __date){
    let result = true;
    if(!Number.isInteger(__year * 1)){
      console.log("첫번째 파라미터는 년도에 해당하므로 숫자형태로만 입력하셔야 합니다!");
      result = false;
    }

    if(!(__month * 1 >= 1 && __month * 1 <= 12)){
      console.log("두번째 파라미터는 월에 해당하므로 1~12 사이의 숫자만 입력하셔야 합니다!");
      result = false;
    }

    if(!(__date * 1 >= 1 && __date * 1 <= 31)){
      console.log("세번째 파라미터는 일에 해당하므로 1에서 최대 31 까지의 숫자만 입력하셔야 합니다!");
      result = false;
    } else {
      let dmonth = __month * 1;
      let dint = __date * 1;
      let allowdMaxDate;
      switch (dmonth) {
        case 4:
        case 6:
        case 9:
        case 11:
          allowdMaxDate = 30;
          break;
        case 2:
          allowdMaxDate = 28;
          break;
        default:
          allowdMaxDate = 31;
          break;
      }
      if(__date * 1 > allowdMaxDate){
        console.log("일자에 해당하는 값이 잘못 입력되었습니다. 입력된 일자 : " + __date + ", 입력 가능한 최대일자 : " + allowdMaxDate);
        result = false;
      }
    }

    return result;
  };
  this.year = _year * 1;
  this.month = _month * 1;
  this.date = _date * 1;
  this.available = this.checkParams(_year, _month, _date);
}

let tobj1 = new dataExprObj("2017", "2", "24");

생성자임에도 선언은 function 키워드로 하기 때문에 보통은 생성자 함수라고 부릅니다. this 키워드를 사용해서 실행컨텍스트에서 프로퍼티를 저장하도록 처리하죠. 이렇게 선언한 이후 마지막 줄에서 new 키워드로 객체를 생성했네요.

자바스크립트에서 생성자의 의미는 객체를 찍어내기 위한 틀을 만드는 거라고 생각하시면 될거 같습니다~!


※ prototype

생성자함수에는 prototype 이라는 프로퍼티가 자동으로 할당되는데, new 키워드를 통해 생성되는 객체들에게 공통적인 프로퍼티를 할당하기 위해 사용됩니다. 예제를 보도록 하죠.

dateExprObj.prototype.addYear = function(inc_year){
  if(!this.available) return false;
  this.year = this.year * 1 + inc_year;
}

dateExprObj.prototype.subYear = function(sub_year){
  if(!this.available) return false;
  this.year = this.year * 1 - sub_year;
}

dateExprObj.prototype.addMonth = function(inc_num){
  if(!this.available) return false;

  if(this.month + inc_num > 12){
    this.year += Math.trunc((this.month + inc_num) / 12);
    this.month = (this.month + inc_num) % 12;
  } else if(this.month + inc_num < 1){
    this.year += Math.trunc(((this.month + inc_num) / 12) - 1);
    this.month = (this.month + inc_num) % 12 + 12;
  }
}

dateExprObj.prototype.subMonth = function(sub_num){
  if(!this.available) return false;
  this.addMonth(-sub_num);
}

dateExprObj.prototype.toDateStr = function(){
  if(this.available)
    return this.year + "-" + this.month + "-" + this.date;
  else {
    console.log("날짜 파라미터가 잘못 입력되었습니다!");
    console.log("입력된 년도 : " + this.year);
    console.log("입력된 월  : " + this.month);
    console.log("입력된 일  : " + this.date);
  }
}

let obj1 = new dateExprObj("2018", "2", "25");
obj1.addMonth(25);
console.log(obj1.toDateStr());
obj1.subMonth(14);
console.log(obj1.toDateStr());

생성자함수.prototype. 으로 생성할 객체에서 사용할 공통 메소드를 지정하였습니다. 이후 객체를 생성해서 prototype으로 지정한 메소드를 마치 자기가 갖고 있는 메소드처럼 사용하네요.

예제의 실행결과는 다음과 같습니다.


※ 객체와 prototype 의 연결

이제 실제로 생성자함수와 new 키워드로 생성된 객체가 어떻게 생겼는지 한번 까보겠습니다.

앞서 설명드린거 처럼 생성자 함수에 함수에 prototype 이라는 프로퍼티로 공통메소드가 선언되었는데요. new 키워드로 생성된 객체인 obj1은 __proto__ 라는 프로퍼티가 있고, 여기서 생성자 함수의 prototype에 선언된 공통 메소드를 접근하네요.

(obj1.__proto__.__proto__ 를 보면 Object 가 나오죠? 자바스크립트의 모든 객체는 Object 객체를 그 원형으로 두고 있는겁니다.)

여기서 중요한게 바로 __proto__ 입니다. 객체의 원형이라고 볼수도 있는 생성자 함수와 프로토타입을 접근하게 해주는 녀석이거든요. 보통은 생략해서 쓰기 때문에 이렇게 콘솔로그로 찍어보지 않는 이상은 파악이 잘 안되죠.  이걸 도형으로 만들어서 설명한게 있어서 공유합니다.

생성자 함수를 선언하면 prototype 이라는 프로퍼티가 생성됩니다. 이 안에서 new 키워드로 생성할 객체의 공통된 메소드 혹은 속성들을 설정할 수 있죠. 그런 다음 new 키워드로 instance 객체를 생성하면 생성자 함수가 실행되고, __proto__ 라는 프로퍼티가 생성자 함수의 프로토 타입을 참조합니다.  __proto__는 생략되므로 마치 아래와 같은 모습처럼 되는 거죠.

(만약 __proto__를 생략하지 않은 경우는 실행컨텍스트가 달라져서 문제가 될수도 있습니다. 직접 구현해보고 테스트하면서 파악해보세요.)

생성자 함수, prototype, instance 객체의 관계는 이렇게 삼각형 구조처럼 갖추게 됩니다. 자바스크립트에서 객체 생성의 큰 그림은 이렇게 된다고 보시면 됩니다.

기존에

var tmp_obj1 = {
  tval1 : "1234",
  tint1 : 33,
  tf : function (abc) {
    console.log(abc);
  }
};

...

이런식으로 객체를 생성하신 분들도 많으실텐데요. 이건 엄밀히 말하면 JSON(JavaScript Object Notation)방식으로 객체를 간편하게 생성하는 하나의 방법일 뿐, 객체간의 관계를 설명하지는 못한다고 생각하시면 되겠습니다.


※ 레퍼런스

이 글은 인프런 사이트의 '핵심개념을 알아보는 Javascript Flow' 강좌의 내용을 토대로 예제코드를 작성하여 설명하였습니다. prototype 개념을 포함한 기본 자바스크립트를 더 깊이 이해하기를 원하시면 해당 강좌를 수강하시는 것을 권장합니다. 이 강좌는 무료입니다.

 

다음에는 프로토타입 체이닝 등으로 프로토타입을 더 깊게 들어가보겠습니다~

 

 

Posted by kevin.jeong.
,