13. [JavaScript] 객체 지향 프로그래밍__프로토타입

문도연·2022년 6월 9일
0

Chapter1. 객체 지향
Chapter1-1. 클로저 모듈 패턴
Chapter1-2. 클래스와 인스턴스
Chapter1-3. 객체 지향 프로그래밍
Chapter1-4. 객체 지향 차이점
Chapter2. 프로토타입
Chapter2-1. 프로토타입과 클래스
Chapter2-2. 프로토타입 체인


Chapter2 프로토타입

  • 프로토타입이 무엇인지 설명할 수 있다.
  • 프로토타입과 클래스의 관계에 대해 설명할 수 있다.
  • 프로토타입 체인에 대해 설명할 수 있다.

Chapter2-1. 프로토타입과 클래스

MDN에서 정의하는 프로토타입

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

  • JavaScript는 프로토타입 기반 언어(prototype-based language)라 불림.

    • 이는, 객체들이 메소드와 속성들을 상속받기 위한 템플릿으로써 프로토타입 객체(prototype object)를 가진다는 의미이다.
    • 여기서 프로토타입(Prototype)원형 객체를 의미함

클래스와 프로토타입 : 1+1인가..분신..? 유전자..?

클래스는 함수이다.
자바스크립트에서 함수는 객체이고, 객체는 property를 갖는다.

JavaScript의 함수는 다른 모든 객체처럼 속성과 메서드를 가질 수 있으므로 일급(first-class) 객체입니다. 다른 객체와 함수를 구별하는 것은, 함수는 호출할 수 있다는 점입니다. 함수는 Function 객체입니다.(MDN)

모든 함수는 Function 타입에 대한 인스턴스 객체이며 다른 참조 타입과 마찬가지로 프로퍼티/메서드가 있습니다.

즉, 키워드 class를 사용해 함수를 선언하는 것 === 객체를 생성하는 것.
이 객체가 생성되면 prototype이라는 property를 갖게 된다.

예를 들어, Person이라는 클래스를 구현한다면
-> 클래스는 함수이고, 자바스크립트에서 함수는 곧 객체니까, Person이라는 객체가 생성이 된 것과 다름없음.
-> 이 Person 객체는 propertyprototype 을 갖는다.
-> 근데, 이 prototype은 객체임.(mdn왈 : 원형객체)
-> 그래, prototype은 객체니까, 또 본인만의 property를 갖는다.
-> 본인만의 property가 바로 constructor임.
-> 근데, 이 constructor가 바로 Person이라고 한다.

더 짧게 말하면, Person이라는 클래스를 만들어 객체를 하나 생성했는데, 1+1 행사로 prototype이라는 객체도 하나 생성된 거임

Person이랑 prototype 각각 타입이 객체, 원형객체 이니까 각자 지만의 property를 갖는다는 거임

Personpropertyprototype 이고
prototypepropertyconstructor(=== Person)라는 거임

그림으로 이해하기

코드로 이해하기

class Person {
  constructor(name, age, height) {
    this.name = name;
    this.age = age;
    this.height = height;
  }

  sleep() {
    console.log(`${this.name}은 잠에 들었습니다`);
  }
}

let doni = new Person('문도연', 28, 163);


Person.prototype.constructor === Person; //true
Person.prototype.sleep === doni.sleep; //true
Person.prototype === doni.__proto__;  //true(밑에서 다룸)

콘솔창

클래스 Person의 속성에 prototype이 있는 걸 확인 할 수 있음

Person.prototype은 객체이며, 속성에 constructor가 있는 걸 확인할 수 있음


않이, 그래서 프로토타입 왜 씀? 뭐가 좋길래

  • 상속할 때 메모리 덜 먹어서

  • 클래스 만들 때 클래스 함수 내부에 메서드를 포함하냐 불포함하냐에 따라 메모리 사정이 달라진대

    • 경우 1️⃣
      Person 클래스 안에 sleep() 메서드 포함해서 클래스 만들고 인스턴스 생성하는 거
    • 경우 2️⃣
      Person 클래스 안에 메서드 포함하지 않고, 따로 Person.prototype에다가 sleep() 메서드를 생성하고, 인스턴스 생성하는 거
  • 경우 1️⃣ 메모리를 더 많이 먹는다고 함

코드로 이해하기 - (경우2️⃣)


class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
/*  
    sleep() {
    console.log(`${this.name}은 잠에 들었습니다`);
  }
*/
}
Person.prototype.height = '180cm';
Person.prototype.sleep = function(){
    console.log(`${this.name}은 잠에 들었습니다`);
};

let koo = new Person('구씨', 30);

koo.sleep(); // 구씨은 잠에 들었습니다

경우1️⃣과 경우2️⃣의 차이점

  • 경우1의 클래스로부터 생성된 doni는 height 속성, sleep()메서드를 직접 가진다.
  • 경우2의 로부터 생성된 koo는 height속성, sleep()메서드를 직접 갖지 않는다.
    • koo는 prototype 객체가 갖고 있는 height, sleep()메서드를 참조한 것이기 때문

콘솔창 - 경우1️⃣

콘솔창 - 경우2️⃣


클래스와 프로토타입, 인스턴스의 관계 - 경우2️⃣

객체는 property를 가진다고 했지.
new 키워드로 새 인스턴스 객체(koo)가 생성되면 이 객체도 자기만의 property를 갖겠지.
-> 그 property__proto__라고 함.
-> __proto__는 prototype 객체를 가리킨다.

그림으로 이해하기

콘솔창으로 이해하기

__proto__

__proto__ 를 활용하여 상속 관계를 확인할 수 있다.

알아둬야 할 것

☝🏻 자바스크립트는 클래스가 없다.
ES2015부터 class 키워드를 지원하기 시작했으나, 문법적인 양념일 뿐이며 자바스크립트는 여전히 프로토타입 기반의 언어다.[MDN]

☝🏻 각각의 객체는 [[Prototype]]이라는 은닉(private) 속성을 가진다.

참고

[MDN]Object prototypes
[MDN]함수-javascript
Javascript Function - Function 타입
Javascript proto vs prototype 차이
코딩애플 - 이거보고 prototype 이해 못하면 강의접음


Chapter2-2. 프로토타입 체인

class문법으로 상속

객체 지향 프로그래밍의 특성 중 상속을 JavaScript에서 구현할 때에는 프로토타입 체인을 사용한다

extendssuper 키워드를 이용해서 상속을 구현할 수 있음

그래서 프로토타입 체인 어쩌라궤.. 그게 몬데

객체한테 '너 키 몇이냐?' 물어봤을 때, 객체 본인한테 키 속성이 없는 경우,
-> 부모한테 가서 물어보고, 부모한테 키 속성이 없는 경우,
-> 조부모한테 가서 물어보고, 없으면
-> 그 위에 부모한테 물어보고, 없으면
-> 그 위에 부모한테 물어보는 ...
이런 과정이 바로 프로토타입 체인

프로토타입 체인 예시 - 경우2️⃣

경우2️⃣ 에서
인스턴스 객체(koo)한테 '야 너 키 몇이냐' 물어보면,
대답을 못한다.
koo는 '이쒸 부모(Person)한테가서 물어보면 되지!!' 하고 부모 속성에 키 속성이 있는지 찾아본다. (정확히는 Person.prototype한테 가서 찾아봄)
없으면 할아버지(의 prototype)한테 가서 물어본다.
없으면 증조할아버지(의 prototype)한테 가서 물어본다.
없으면 고조할아버지(의 prototype)한테 가서 물어본다.
...
있으면,,
키 속성 값 '180cm'를 당당히 가져와서 내민다.

자바스크립트 객체는 속성을 저장하는 동적인 "가방"과 (자기만의 속성이라고 부른다) 프로토타입 객체에 대한 링크를 가진다. 객체의 어떤 속성에 접근하려할 때 그 객체 자체 속성 뿐만 아니라 객체의 프로토타입, 그 프로토타입의 프로토타입 등 프로토타입 체인의 종단에 이를 때까지 그 속성을 탐색한다.[MDN]

프로토타입 객체에 대한 링크__proto__을 의미함

예시

학생(Student)과 사람(Human)이라는 클래스가 각각 존재한다고 가정하겠습니다.

학생은 학생이기 이전에, 사람입니다. 따라서 클래스 Student는 Human의 기본적인 메서드를 상속받을 수 있음

다만, 학생은 일반적인 사람의 특징에 추가적인 특징이 필요합니다.

Student는 Human의 특징을 그대로 물려받습니다. 이렇게 속성과 메서드를 물려주는 클래스를 부모 클래스(여기서는 Human), 속성과 메서드를 물려받는 클래스를 자식 클래스(여기서는 Student), 그리고 이 과정을 상속이라고 합니다.

1. 부모 클래스인 Human 을 만들자.

  • 속성 5개, 메서드 1개

class Human { 
    constructor(first, last, age, gender, interests) {
        this.name = {
            first,
            last
        };
        this.age = age;
        this.gender = gender;
        this.interests = interests;
    }
    
    아침인사() {
        console.log(`굿모닝 오늘도 ${this.name.first} 힘내세요`);
    };
}

2. 키워드 extends를 사용하여 자식 클래스 Student에 부모의 기본 속성을 상속한다.

-> 자식 클래스만 가지는 속성 2개/메서드 1개 추가

class Student extends Human {
    constructor(first, last, age, gender, interests, 취미, 목표기업 ) {
        this.name = {
            first,
            last
        };
        this.age = age;
        this.gender = gender;
        this.interests = interests;
		
        this.취미 = 취미; //자식 클래스만이 가지는 속성추가
        this.목표기업 = 목표기업; //자식 클래스만이 가지는 속성추가
     }  
        아침인사() {
        console.log(`굿모닝 오늘도 나 ${this.name.first}을 보며 힘내세요 ~`);
        };
        
        //자식 클래스만이 가지는 메서드추가
        짜증내기() {
        console.log(`${this.목표기업} 준비하려면 ${this.취미}는 잠시 내려놔야하는 걸까?`);
        };	
}

3. 2번 코드는 너무 김

-> 2번 코드에 키워드super를 추가하면 코드를 더 간결하게 쓸 수 있음

단, constructor()의 첫번째로 super를 넣어야 함.

class Student extends Human {
    constructor(first, last, age, gender, interests, 취미, 목표기업 ) {
        super(first, last, age, gender, interests);
      
		//자식 클래스만이 가지는 속성
		this.취미 = 취미; 
		this.목표기업 = 목표기업; 
		}    
        
		//자식 클래스만이 가지는 메서드
   		 짜증내기() {
   		 console.log(`${this.목표기업} 가고싶어. ${this.취미}는 조금씩 즐기자`);
    };	
    
}

super를 쓰면 부모 클래스의 생성자를 호출하고, super()의 매개변수를 통해 부모 클래스의 멤버를 상속받을 수 있음.

4. 상속이 잘 됐는지 확인하기 위해 콘솔창으로

-> 자식 클래스 Student의 인스터스 생성해보기

1) 부모 클래스 Human 입력

2) 자식 클래스 Student 에 키워드 extends, super 사용해서 부모 클래스로부터 상속받기

3) 자식 클래스 Student의 자식 인스턴스 생성하기

4) 부모 클래스의 기본 속성/메서드를 3)의 인스턴스가 잘 불러오는지 확인

5) 자식 클래스의 속성/메서드를 3)의 인스턴스가 잘 불러오는지 확인

-> Student 인스턴스 잘 생성됨.
-> 의도한대로 이제 부모 클래스 Human과 자식 클래스 Student 양쪽의 속성과 메소드를 사용할 수 있게 됨.

Getters와 Setters

생성한 인스턴스의 속성 값을 바꾸고자 할 때 사용할 수 있음

코딩공부를 하다가 문도연의 목표기업이 바뀔 수 있자네?
-> 이 때 필요한게 게터와 세터

클래스 Student 에 getter/setter 추가해줘야 함. 이 둘은 쌍으로 동작한대.

getter가 현재 값을 리턴하고 setter는 값을 변경함

위에서 실습한 코드 가져와서 수정해보자

class Student extends Human {
    constructor(first, last, age, gender, interests, 취미, 목표기업 ) {
        super(first, last, age, gender, interests);
		
        //자식 클래스만이 가지는 속성
        this.취미 = 취미; 
        this._목표기업 = 목표기업; 
        }    

        get 목표기업() {
            return this._목표기업;
        }

        set 목표기업(new목표기업) {
            this._목표기업 = new목표기업;
        }
    
        //자식 클래스만이 가지는 메서드
        짜증내기() {
        console.log(`${this.목표기업} 가고싶어. ${this.취미}는 조금씩 즐기자`);
        };	
    
}

목표기업 속성에 대해 게터와 세터 생김
속성에는 _를 붙여야 함. 안 붙이면 게터/세터 호출할 때 에러 발생함

  • 문도연 인스턴스의 목표기업 속성 값을 조회하려면
    -> 문도연._목표기업

  • 목표기업 에 새 값 할당하려면
    -> 문도연._목표기업='새로 목표한 기업이름'

잘 되는지 확인하기 위해 콘솔로 가보자

1) Student 클래스에 대해 인스턴스 생성

게터/세터 추가한 Student 클래스에 대해 문도연 인스턴스 생성

2) 인스턴스 속성 초기값 조회

3) 변경 => 변경됐는지 조회

참고

[MDN]ECMAScript 2015 클래스
[MDN]상속과 프로토타입


DOM과 프로토타입

DOM도 상속 관계로 이루어져있습니다.
proto 를 활용하여 상속 관계를 확인할 수 있습니다.

브라우저에서 DOM을 이용하면, document.createElement('div')로 새로운 div 엘리먼트를 만들 수 있습니다.

이렇게 생성된 div 엘리먼트는, HTMLDivElement라는 클래스의 인스턴스입니다.

DOM 엘리먼트는 예를 들어 innerHTML과 같은 속성, 또는 append()와 같은 메서드가 있습니다.

각각의 엘리먼트가 해당 메서드나 속성이 있다는 것을 통해, Element라는 공통의 부모가 있음을 알 수 있습니다.

화살표 방향은 부모를 가리킵니다. EventTarget의 부모로는, 모든 클래스의 조상인 Object가 존재합니다.

인스턴스의 proto를 이용하면 부모 클래스의 프로토타입, 혹은 '부모의 부모 클래스'의 프로토타입을 탐색할 수 있습니다.

let div = document.createElement('div');

div.__proto__ 
div.__proto__.__proto__
div.__proto__.__proto__.__proto__
div.__proto__.__proto__.__proto__.__proto__
div.__proto__.__proto__.__proto__.__proto__.__proto__
div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__
div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__

콘솔창으로 가보자

profile
중요한건 꺾이지 않는 마음이 맞는 것 같습니다

0개의 댓글