Front-end/JavaScript

[JavaScript] 상속(inheritance), 프로토타입

윤민_ 2021. 11. 6. 00:01

상속이란?

객체는 연관된 로직들로 이루어진 작은 프로그램이라고 할 수 있다. 상속은 객체의 로직을 그대로 물려받는 또 다른 객체를 만들 수 있는 기능을 의미한다. 단순히 물려받는 것이라면 의미가 없을 것이다. 기존의 로직을 수정하고 변경해서 파생된 새로운 객체를 만들 수 있게 해 준다. 

 

쉽게 생각하기 위해 예제를 들어보자

 

객체는 하나의 컨테이너라 생각해 본다. 그 안에는 변수, 메소드가 포함되어 있다.

이때 객체의 특성을 통해 기존 객체를 물려받은 새로운 객체를 만들 수 있다.

이때 물려 받은 아들 객체(새로운 객체)는 부모 객체(원본 객체)와 동일한 기능을 가지게 된다.

아들 객체(새로운 객체)는 부모 객체(원본 객체)의 특정 기능은 제거, 추가하여 자신의 맥락에 맞게 재활용하게 된다.이러한 과정이 상속의 기본적인 동작 방법이라 할 수 있다. 중요한 점은 로직이 재활용이 가능하다는 점이다!

 

function Person(name){
    this.name = name;
    this.introduce = function(){
        return 'My name is '+this.name; 
    }   
}
var p1 = new Person('yoonmin');
document.write(p1.introduce()+"<br />");

//출력: MY name is yoonmin

하단의 코드는 상단의 코드의 일부분을 바꾼 것이다.

function Person(name){
    this.name = name;
}
Person.prototype.name=null;
//person객체 안에는 prototype이라는 약속된 프로퍼티(객체)가 존재한다.
Person.prototype.introduce = function(){
    return 'My name is '+this.name; 
}
var p1 = new Person('yoonmin');
document.write(p1.introduce()+"<br />");

두 코드는 결과는 같다. 하지만 상속을 위한 기본적인 준비를 한 것이다.

function Person(name){
    this.name = name;
}
Person.prototype.name=null;
Person.prototype.introduce = function(){
    return 'My name is '+this.name; 
}
 
function Programmer(name){
    this.name = name;
}
Programmer.prototype = new Person();
 
var p1 = new Programmer('yoonmin');
document.write(p1.introduce()+"<br />");

Programmer이라는 생성자를 만들었다. 그리고 이 생성자의 prototype과 Person의 객체를 연결했더니

Programmer 객체도 메소드 introduce를 사용할 수 있게 되었다.

Programmer가 Person의 기능을 상속하고 있는 것이다. 단순히 똑같은 기능을 갖게 되는 것이라면

상속의 의의는 사라질 것이다. 부모의 기능을 계승 발전할 수 있는 것이 상속의 가치다.

 

function Person(name){
    this.name = name;
}
Person.prototype.name=null;
Person.prototype.introduce = function(){
    return 'My name is '+this.name; 
}
 
function Programmer(name){
    this.name = name;
}
Programmer.prototype = new Person();
Programmer.prototype.coding = function(){
    return "hello world";
}
 
var p1 = new Programmer('yoonmin');
document.write(p1.introduce()+"<br />");
document.write(p1.coding()+"<br />");

기존의 부모 객체에서 추가된 기능 메소드 coding을 추가해주었다.


prototype

prototype이란 무엇인가?

한국어로는 prototype은  원형(원래의 형태)이다. 말그대로 객체의 원형이라고 할 수 있다. 함수는 객체다.

그러므로 생성자로 사용될 함수도 객체다. 객체는 property(Property 는 속성 이란 뜻으로, JS에서는 객체 내부의 속성을 의미합니다.)를 가질 수 있는데 prototype이라는 property는 그 용도가 약속되어 있는 특수한property이다.  prototype에 저장된 속성들은 생성자를 통해서 객체가 만들어질 때 그 객체에 연결된다. 

 

function Ultra(){}
Ultra.prototype.ultraProp = true;
 
function Super(){}
Super.prototype = new Ultra();
 
function Sub(){}
Sub.prototype = new Super();
 
var o = new Sub();
console.log(o.ultraProp);
/*
Ultra(ultraprop =True)   Super              Sub

sub는 super를 상속받고 / super는 ultra를 상속받는다
*/

생성자는 기본적으로 함수이다. 이때 이 함수를 호출할때 앞에 new를 붙여준다면 생성자가된다.

즉, 생성자(객체)가 된다는 것이다.


생성자 Sub를 통해서 만들어진 객체 o가 Ultra의 프로퍼티 ultraProp에 접근 가능한 것은 prototype 체인으로 Sub와 Ultra가 연결되어 있기 때문이다. 내부적으로는 아래와 같은 일이 일어난다.

  1. 객체 o에서 ultraProp를 찾는다.
  2. 없다면 Sub.prototype.ultraProp를 찾는다.
  3. 없다면 Super.prototype.ultraProp를 찾는다.
  4. 없다면 Ultra.prototype.ultraProp를 찾는다.

prototype는 객체와 객체를 연결하는 체인의 역할을 하는 것이다. 이러한 관계를 prototype chain이라고 한다.

 

Super.prototype = Ultra.prototype 으로하면 안된다. 이렇게하면 Super.prototype의 값을 변경하면 그것이 Ultra.prototype도 변경하기 때문이다. Super.prototype = new Ultra();는 Ultra.prototype의 원형으로 하는 객체가 생성되기 때문에 new Ultra()를 통해서 만들어진 객체에 변화가 생겨도 Ultra.prototype의 객체에는 영향을 주지 않는다.

그렇다면 프로토타입을 사용하지않고 생성자 함수 안에서 메소드나 속성을 직접 정의 하게 되면 어떤 비효율이 발생할까? 

 

생성자 함수 안에서 직접 정의하게되면, new 키워드로 객체를 만들  마다 함수를 정의해주어 메모리 낭비를 하게된다. 

그래서 생성자함수명.protopyte.함수명 = function(){}   번만 정의해주고, 생성자함수를 통해 생성된 객체들이 모두 사용   있게된다. 수정시에도 해당프로토타입 함수만 수정하면 되므로 유지보수에도 효율적이다.

그리고 만약에 객체가 개별적으로 동일한 이름의 함수를 정의해서 사용한다면 해당 객체가 가진 함수를 먼저 확인하고 실행하기때문에protopyte으로 만든 함수와 함수를 사요하는 다른 객체에 영향을 주지않는다.

function Person(name,first,second){
	this.name = name;
	this.first = first;
	this.second = second;
}

Person.prototype.sum = function(){
		return this.first + this.second;
	}	
//prototype 메소드를 선언해줌으로써 성능 개선, 메모리절약, 유지보수가 쉽다는 장점이있다.

yoon.sum = function(){
    return 'this'+ (this.first +this.second);
}
//이처럼 yoon이란 변수를 재정의하기 쉽다.

var yoon = new Person('yoon', 10, 20);
var min = new Person('min', 10, 10);
console.log(yoon.sum());
console.log(min.sum());

 

(+추가) 메소드와 프로퍼티의 차이는?

property 속성이라면 method 행동 입니다.

예를 들어 listA 라는 array 가 있을 때, listA.length 는 property 죠? 그렇지만 listA.push(1) 은 method 입니다.

 

구분 방법으로

그래서 어떻게 구분하면 좋을까요?

기본적으로 두가지 방법이 있습니다. 의미로 구분하는 것과 사용방법으로 구분하는 것이있다.

 

사용방법 구분으로는

javascript method 는 함수로 된 property이다. 함수의 가장 큰 특징! callable(호출할수있는) 하다는 것입니다.

따라서 javascript 에서는 괄호로 함수를 호출하므로 대부분 괄호로 끝나면 method, 없으면 property 라 보아도 무방합니다.