상속, Prototype (객체지향 2)

생성자를 통해 인스턴스를 만든다는 것은 생성자라는 부모를 상속받는 것과 같다. 따라서 생성자의 여러 속성들을 사용할 수 있는 것이다. 이때 상속을 할 수 있는 방법 중 자바스크립트 언어만이 가지고 있는 특징이 하나 있다. 그것이 바로 Prototype이다. 프로토타입의 뜻은 원형, 표준 예라고 할 수 있으나 좀 더 와닿는 표현이 있다. 강의에서는 유전자라고 표현하였다.

 

1. Prototype, 자바스크립트에서의 상속

function Student(이름){
        this.name = 이름; 
        this.age = 15;
        this.sayHi = function(){ 
            console.log('안녕하세요 ' + this.name + ' 입니다.');
        };
       
 Student.prototype.gender = '남'
 
 var 학생1 = new Student('손흥민');
 console.log(학생1.gender) // '남'
  • 생성자를 만들면 자동으로 prototype이라는 속성이 추가된다. 즉 Student라는 생성자에는 name, age, sayHi 뿐만 아니라 prototype이 새로 정의된다.
    • prototype에 값을 추가하면 생성자를 통해 생성되는 인스턴스(자식)에게 물려줄 수 있다. 즉, 상속이 가능하다.
  • 위의 경우 Student의 prototype에 접근하여 gender라는 키와 '남'이라는 값을 추가하였다. 이는 학생1에 상속되어 gender라는 값을 갖게 된다.
    • 하지만 학생1을 출력하면 gender가 추가되어 있지는 않다. 그저 부모 생성자의 유전자(prototype)에 추가하였기 때문에 사용할 수 있는 것이다.

2. Prototype의 동작원리

  • 자바스크립트는 내장함수나 속성을 찾을 때 부모의 prototype을 찾는다.
    1. 부모의 prototype을 찾는다.
    2. 없을 경우 부모의 부모 prototype을 찾는다.
console.log(학생1.name);
console.log(학생1.gender);
학생1.toString();
  • 학생1의 경우 name 값을 가지고 있기 때문에 바로 출력이 된다.
  • 하지만 학생1은 gender라는 값을 가지고 있지 않다.
    • 따라서 학생1의 부모 Student의 prototype을 확인한다. 만약 prototype에 gender가 있으면 그 값을 가져와서 사용한다. (유전자와 같은 개념)
  • toString()의 경우는 오브젝트의 내장함수이다. 학생1에게는 toString이라는 함수가 없다.
    • 학생1의 부모 Student의 prototype을 확인한다. Student에겐 toString이 정의되어 있지 않다.
      • Student의 부모인 Object의 prototype에 toString이 있음을 확인한다.
        • 있다면 학생1은 toString을 사용할 수 있다.

etc-image-0
웹 브라우저 콘솔 창에서 확인할 수 있다.

  • 이처럼 자바스크립트는 계속해서 건너건너 부모의 prototype을 확인한다.

3. 오브젝트와 Prototype

var arr = [1,2,3];
var arr = new Array(1,2,3); // 자바스크립트가 만드는 방식

arr.sort() // 부모(Array)에 있기 때문, Array.prototype에는 여러 함수들이 담겨 있음.
var obj = { name : 'Kim' };
var obj = new Object();
  • 배열이나 오브젝트를 생성할 때 편하게 [ ] 나 { } 를 사용하지만 사실 이는 new 생성자를 통해 만드는 것이다.
  • Prototype은 변수에서는 생성되지 않는다. 함수에서만 prototype이 생성된다.
  • 위처럼 arr이나 obj 같은 변수에는 prototype이 존재하지 않는다. 그저 부모의 prototype을 사용하는 것일 뿐이다.
    출력시 undefined.
  • 내 부모 유전자(prototype)를 확인하고 싶으면 __proto__ 를 사용하면 된다. __proto__는 부모의 prototype을 나타낸다.
// 내 부모 유전자(prototype)을 확인하고 싶다면 __proto__
    console.log(학생1.__proto__) // Student 생성자가 출력됨

    // 부모님 강제 등록하기
    var 부모 = { name : 'Kim' };
    var 자식 = {};
    자식.__proto__ = 부모; // 자식이라는 변수의 부모를 부모 변수로 함
    자식.name; // 'Kim'
  • __proto__를 사용하면 강제로 상속을 할 수 있다.
  • 자식.__proto__란 자식의 부모 prototype을 부모라는 오브젝트로 설정한다는 뜻이다. 따라서 자식은 부모의 name 속성을 가질 수 있다.

4. Prototype 예제

function Parent(){
  this.name = 'Kim';
}
var a = new Parent();

a.__proto__.name = 'Park';
console.log(a.name)
  • 변수 a가 Parent 생성자를 통해 name = 'Kim'이라는 값을 갖는다. 이 때 a.__proto__.name을 통해 'Park'이라는 값을 저장한다. 하지만 a의 name은 변경되지 않는다. a.__proto__는 a의 생성자, Parent의 prototype을 가리킨다. a.__proto__.name은 Parent의 prototype에 name 값을 추가한 것이다. 만약 a에 name이 없을 경우에는 부모의 prototype을 찾아 Park을 출력한다. 하지만 a에는 이미 'Kim'이라는 name이 있으므로 이것을 우선 출력한다. ( 2. Prototype의 동작 원리 )
function Student(이름, 나이){
  this.name = 이름;
  this.age = 나이;
}

Student.prototype.sayHi = () => {
    console.log('안녕 나는 ' + this.name + '이야');
  }
var 학생1 = new Student('Kim', 20);

학생1.sayHi();
  • Student의 prototype에 sayHi라는 함수를 추가하였다. 하지만 this.name이 출력되지 않는다. 그 이유는 Arrow Function을 사용하였기 때문이다. Arrow Function을 사용하면 this의 의미는 일반함수와는 다른 의미를 갖는다. 여기서 this는 함수 밖에서의 this, 즉 window를 가리키게 된다.
var arr = [1,2,3];
arr.remove3();

console.log(arr);
Array.prototype.remove3 = function(){
  for (var i = 0; i < this.length; i++) {
    if ( this[i] === 3 ) {
      this.splice(i,1);
    }
  }
};

var arr = [1,2,3,4];
arr.remove3();
  • arr에 remove3이라는 함수를 추가해서 사용하고 싶은 경우이다. 이때 arr은 new Array로 생성한 것과 같기 때문에 부모가 Array이다. 따라서 함수를 추가하려면 Array의 prototype에서 추가해야 한다. (일반 변수는 prototype이 없음) 여기서 Array 객체를 가리키기 위해서는 Arrow Function이 아닌 일반 함수로 작성하여 this를 사용한다. 이 때 this는 오브젝트 Array를 뜻한다.

 

자료 출처 : https://codingapple.com/

 

코딩애플 온라인 강좌 - 개발자도 단기완성!

단연 NO1 강사님의 NO.1 강의 역시나 명강입니다. IT 업계의 대치동 NO1. 강사같은 엄청난 강의력. 코딩애플님의 강의는, 엄청나게 기초적인 것부터 가르치는 듯 보이지만, 실제로 다루는 깊이는 절

codingapple.com