[TIL] 2020. 06. 17. Prototype_Constructor_Inheritance

달밤·2020년 6월 17일
0

TIL

목록 보기
43/110
post-thumbnail

오늘 배운 것

1. Prototype 과 Inheritance

JavaScript에서는 객체를 상속하기 위해서 프로토타입이라는 방식을 사용한다.

JavaScript는 흔히 프로토타입 기반 언어(prototype-based language)라 불린다— 모든 객체들이 메소드와 속성들을 상속 받기 위한 템플릿으로써 프로토타입 객체(prototype object)를 가진다는 의미

프로토타입 객체도 또 다시 상위 프로토타입 객체로부터 메소드와 속성을 상속 받을 수도 있고 그 상위 프로토타입 객체도 마찬가지이다. 이를 프로토타입 체인(prototype chain)이라 부른다.

정확히 말하자면 상속되는 속성과 메소드들은 각 객체가 아니라 객체의 생성자의 prototype이라는 속성에 정의되어 있습니다.

참고 : MDN

  • 프로토타입(Prototype)은 객체들이 메소드와 속성들을 상속받기 위해 이용하는 하나의 객체이다. 프로토타입은 객체의 생성자의 속성(프로퍼티)에 정의되어 있는 하나의 객체라고 볼 수 있다.
  • 생성자.prototype을 통해 프로토타입 객체에 접근할 수 있으며, 이 객체에는 해당 생성자를 통해 생성된 객체들이 사용할 수 있는 메소드와 constuctor가 정의 되어있다.
  • constructor는 생성자를 가리키며 prototype의 속성이며, 생성자.prototype.constructor === 생성자이다. 즉 prototype과 constructor는 서로를 참조한다.
  • 객체의 생성자에 정의되어 있는 속성을 통해 접근할 수 있는 프로토타입 객체는 인스턴스에서도 접근할 수 있다. 생성자(constructor)에서 생성자.prototype으로 접근했다면 인스턴스.__proto__를 통해서 프로토타입 객체에 접근이 가능하다. 즉 __proto__ 속성을 통해서 자신을 생성한 생성자, 즉 부모의 프로토타입 객체로 접근할 수 있게 된다. 이를 prototype link라고 부른다. 참고로 __proto__속성은 모든 객체가 갖는 속성이다. 따라서 프로토타입 객체도 가지고 있으며, 생성자 객체와 인스턴스 객체 모두가 가지고 있다.
let A = String("안녕하세요") // A = "안녕하세요" 와 동일하다
//A는 인스턴스이고, String은 생성자이다. 
//인스턴스 객체는 __proto__속성을, 생성자 객체는 prototype 속성을 가진다. 
//두 가지 속성 모두 같은 프로토타입 객체를 가리킨다.
A.__proto__ === String.prototype //true
//프로토타입 객체에서는 String 생성자를 통해 생성된(상속받은) 객체들이 
//사용할 수 있는 메소드들이 정의 되어있다.
A.__proto__.constructor === String //true
String.prototype.constructor === String
//프로토타입 객체에서 생성자로 접근할 수 있수도 있고, 생성자에서 프로토타입 객체로 접근할 수도 있다.
//즉 프로토타입 객체와 생성자(constructor)는 서로를 참조한다.
A.__proto__.__proto__ //Object의 프로토타입 객체
//프로토타입 객체에도 상속받은 메소드들이 정의되어 있는 __proto__속성이 있으며 
//이를 통해 상위로 올라갈 수 있다.
//가장 상위에 있는 프로토타입 객체는 Object 생성자의 프로토타입 객체임을 알 수 있다.
//이 밖에 Array,Function,Number와 같은 생성자도 
//프로토타입을 타고 상위로 올라가게 되면 Object 객체를 상속받는 것을 알 수 있다. 
//자바스크립트의 거의 모든 요소들은 객체이고, 객체를 상속받기 때문에 객체 메소드를 사용할 수 있다.
//String 인스턴스의 경우 String,Object를 상속받기 때문에 
//각각의 프로토타입에 정의되어 있는 전부 메소드들을 사용할 수 있다.

//다음의 경우도 마찬가지다.
function Person(name){
    this.name = name;
}

let soohyun = new Person("soohyun")
//이 경우 soohyun은 인스턴스, Person은 생성자(constructor)이다.
Person.prototype.sleep = function(){console.log("졸려요")}
//생성자 Person의 프로토타입에 sleep라는 이름의 메소드(함수)를 정의해주었다.
//앞으로 Person 생성자를 통해 생성되는 모든 인스턴스들은 sleep 메소드를 사용할 수 있다.
//Person.prototype이나 soohyun.__proto__를 통해 프로토타입 객체에 접근할 수 있다.

Person.prototype.constructor === Person //true

2. Object.create()

기본적으로 프로토타입(Prototype)은 할당이 가능하다.

여기서는 Object.create()를 이용한 고전적인 상속방법에 대해 알아본다.

참고: MDN

function Human(name){
    this.name = name;
}
Human.prototype.sleep = function(){console.log("자는 중입니다")}

function Student(name){
    
}

//Human과 Student 생성자 함수를 각각 만들었다.
//Student가 Human의 프로토타입을 상속받게 하기위해서는 어떻게 해야할까

//Student.prototype = Human.prototype 
//단순히 Human의 프로토타입객체를 Student의 프로토타입 객체에 할당하는 것만으로는 충분치 않다.
//이 경우 Human의 프로토타입 객체와 Student의 프로토타입의 객체가 완전히 같아지게된다.
//따라서 Student의 프로토타입 객체에 메소드를 추가하면, Human의 프로토타입에도 추가된다.
//즉 Student 프로토타입과 Human 프로토타입의 참조가 같아진다.

//우리가 원하는 것은 Student가 Human을 상속받되, 
//Student 고유의 메소드도 사용하는 것이다.

Student.prototype = Object.create(Human.prototype)
//Object.create()를 통해 Student의 protoype에 Human prototype을 할당할 수 있다.
//이 경우 Human 프로토타입의 내용을 복사해 Student 프로토타입에 복사하는 것과 같다.
//즉 Human과 Student 프로토타입의 참조가 각각 다르게 되며, 
//Student에서 고유의 메소드를 사용할 수 있다.

Student.prototype.learn = function(){console.log("배우는 중입니다")}
let soohyun = new Student("soohyun")
soohyun.sleep() //"자는 중입니다."
soohyun.learn() //"공부하는 중입니다."

soohyun.__proto__.constructor // Human 생성자
//하지만 이 경우에도 constructor가 여전히 Human 함수를 바라보고 있다.

Student.prototype.constructor = Student;
//construtor를 Student 함수로 바꾸어주었다.

soohyun.name //undefined
//하지만 또 this 바인딩이 제대로 되지 않아 파라미터의 name이 잘 전달되지 않았다.

function Student(name){
    Human.call(this, name) //Human.apply(this, arguments)
}
//this 바인딩을 통해 파라미터가 제대로 전달되게끔 해주면 끝이 난다.

//아래는 모범답안 :)
function Human(name){
    this.name = name;
}

Human.prototype.sleep = function(){console.log("자는 중입니다")}

function Student(name){
    Human.call(this, name) //Human.apply(this, arguments)
}

Student.prototype = Object.create(Human.prototype)
Student.prototype.constructor = Student;

Student.prototype.learn = function(){console.log("배우는 중입니다")}

3. ES6 Class와 Super

Object.create()를 이용해 상속을 하고자 할 경우 지정해줘야 할 것들이 너무 많아 번거롭다고 느꼈을 것이다.

여기서는 상속을 간단하게 해줄 수 있는 ES6 문법인 classsuper에 대해 알아본다.

class Human {	
    constructor(name){
        this.name = name;
    }
    sleep(){console.log("자는 중입니다.")}
}

class Student extends Human {
    constructor(name) { //constructor구문은 부모 클래스와 동일하다면 생략가능
        super(name); 
    }
    learn(){console.log("공부하는 중입니다.")}
}

//function 대신 class키워드를 사용해 class 이름을 지정하고, constructor안에 속성을 정의한다.
//constructor 바깥에는 메소드를 정의한다.
//Human class를 상속한다는 의미에서 extends를 사용했으며
//call,apply대신에 super 키워드를 이용해 바인딩을 해줄 수 있다.


//이 코드의 경우 2번에서 구현한 것과 동일한 기능을 훨씬 간단하게 구현할 수 있다.
profile
다 늦은 밤, 달밤의 개발일기

0개의 댓글