[JS/node] prototype

prototype

원형 객체

필기

  1. 프로토타입은 객체 지향 구현을 위해 사용할 수 있다.
  2. 해당 클래스의 메소드 정보가 담겨 있다.

Achievement Goals

  • Javascript 객체 프로토타입을 이해하고,
  • 프로토타입 체인이 어떻게 동작하는지,
  • 또 프로토타입 속성에 새 메소드를 추가하는 방법을 배웁니다.

prototype 기반 언어, JavaScript

모든 객체들이 메소드와 속성들을 상속 받기 위한 템플릿으로써 프로토타입 객체(prototype object)를 가진다

prototype chain

상속을 JavaScript에서 구현할 때 사용

프로토타입 객체도 또 다시 상위 프로토타입 객체로부터 메소드와 속성을 상속 받을 수도 있고 그 상위 프로토타입 객체도 마찬가지.
다른 객체에 정의된 메소드와 속성을 특정 객체에서 사용할 수 있도록 하는 근간

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

__proto__

[dunder proto]
특정 객체의 프로토타입 객체에 접근 가능하게 하는 속성
(프로토타입 체이닝에 의해서 __proto__를 참조하게 하는 자바스크립트 자체의 작동 원리)

prototype 객체(개별 객체의 속성)

모든 객체들이 가지는 메소드와 속성들을 상속 받기 위한 템플릿
__proto__ 속성으로 접근 가능한 내장 객체

prototype 속성(생성자의 속성)

상속 받은 멤버들(속성, 메소드들)이 정의된 객체의 생성자에 있는 속성
프로토타입 체인을 통해 상속하고자 하는 속성과 메소드를 담아두는 버킷으로 주로 사용되는 객체

예시

Object. (x)
Object.prototype. (o)

개별 객체의 속성

생성자 속성

원본 생성자 함수 자신

프로토타입 체인 동작 증거

person1의 프로토타입 객체인 Person()에 정의된 멤버들
— name, age, gender, interests, bio, greeting을 볼 수 있습니다.
또한 — watch, valueOf처럼 Person()의 프로토타입 객체인 Object에 정의된 다른 멤버들도 보실 수 있습니다. 이는 프로토타입 체인이 동작한다는 증거입니다.

person1.valueOf()

  1. 브라우저는 우선 person1 객체가 valueOf() 메소드를 가지고 있는지 체크합니다.
  2. 없으므로 person1의 프로토타입 객체(Person() 생성자의 프로토타입)에 valueOf() 메소드가 있는지 체크합니다.
  3. 여전히 없으므로 Person() 생성자의 프로토타입 객체의 프로토타입 객체(Object() 생성자의 프로토타입)가 valueOf() 메소드를 가지고 있는지 체크합니다.
  4. 여기에 있으니 호출하며 끝납니다!

Human이라는 클래스와 인스턴스, 그리고 프로토타입의 관계


[그림] 클래스 Human과 인스턴스, 그리고 프로토타입의 관계 모식도

Array(배열) 클래스와 인스턴스, 그리고 프로토타입의 관계


[그림] 배열 arr과 Array, 프로토타입의 관계 모식도


extends 키워드

클래스를 다른 클래스의 자식으로 만들기 위해 class 선언 또는 class 식에 사용

super 연산자

상위 클래스의 생성자를 호출하며 super()의 매개변수를 통해 상위 클래스의 멤버를 상속받을 수 있는 코드

필독 레퍼런스

프로토타입 문서

Reference Code

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Object-oriented JavaScript class further exercises</title>
  </head>

  <body>
    <p>This example requires you to enter commands in your browser's JavaScript console (see <a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools">What are browser developer tools</a> for more information).</p>

  </body>

    <script>
      function Person(first, last, age, gender, interests) {
        this.name = {
          'first': first,
          'last' : last
        };
        this.age = age;
        this.gender = gender;
        this.interests = interests;
        this.bio = function() {
          // First define a string, and make it equal to the part of
          // the bio that we know will always be the same.
          var string = this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. ';
          // define a variable that will contain the pronoun part of
          // the second sentence
          var pronoun;

          // check what the value of gender is, and set pronoun
          // to an appropriate value in each case
          if(this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
            pronoun = 'He likes ';
          } else if(this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
            pronoun = 'She likes ';
          } else {
            pronoun = 'They like ';
          }

          // add the pronoun string on to the end of the main string
          string += pronoun;

          // use another conditional to structure the last part of the
          // second sentence depending on whether the number of interests
          // is 1, 2, or 3
          if(this.interests.length === 1) {
            string += this.interests[0] + '.';
          } else if(this.interests.length === 2) {
            string += this.interests[0] + ' and ' + this.interests[1] + '.';
          } else {
            // if there are more than 2 interests, we loop through them
            // all, adding each one to the main string followed by a comma,
            // except for the last one, which needs an and & a full stop
            for(var i = 0; i < this.interests.length; i++) {
              if(i === this.interests.length - 1) {
                string += 'and ' + this.interests[i] + '.';
              } else {
                string += this.interests[i] + ', ';
              }
            }
          }

          // finally, with the string built, we alert() it
          alert(string);
        };
        this.greeting = function() {
          alert('Hi! I\'m ' + this.name.first + '.');
        };
      };

      let person1 = new Person('Tammi', 'Smith', 32, 'neutral', ['music', 'skiing', 'kickboxing']);
    </script>
</html>

객체지향 왜 해야 해?

기계적 사고방식이 너무 어려웠기 때문에
사람이 사고하는 방식과 비슷하게 만들기 위해서 필요해졌다.

// Case 1. 컴퓨터에게 편한 방식
// assembly === 절차 지향적 언어
메모리 0 - 카운터 할당 = 0
메모리 1 - 카운터 증가
메모리 2 - 출력 'hongsik'
메모리 3 - 카운터와 원하는 반복 횟수 비교
메모리 4 - 카운터 === 원하는 반복 횟수 false? 메모리 1

// 메모리 0 ~ 3을 for문을 통해 반복한다.

// Case 2. 은행 송금 기능
메모리 0 - 송금 주소 할당 = 0000000000
메모리 1 - 전달 받은 주소의 정보를 조회
메모리 2 - 16글자가 넘어서.. 그 다음 메모리 조회
메모리 3 - 16글자가 넘어서.. 그 다음 메모리 조회
...
메모리 10 - 전달 받은 주소 정보 조회 끝!
메모리 11 - 전달 받은 주소로 송금하기 메모리 1259124 조회
하드디스크 1 - ? 아니네
하드디스크 2 - ? 아니네

// Case 3 객체 지향 프로그래밍
// => 컴퓨터 왜 쓰나요? 게임 ,일, => 편할려고, 좀 더 쉽게 
// => 개발 => 상업적 가치
// => 효율적으로 해야 한다. 사람이 쓸 수 있게 개발해야 한다.
// 기계적 사고방식이 너무 어려웠기 때문에
// 사람이 사고하는 방식과 비슷하게 만들기 위해서 필요해졌다.
class 은행계좌 {
   constructor(){

   }

  은행계좌주소: string
  송금 : () => {
    송금에 필요한 코드
  }
  입금 : () => {
    입금에 필요한 코드
  }
}

설명할 줄 안다 기준

A - 주장
R - 근거
E - 예시


Relations of class constructor, prototype, instance

어떻게 서로를 참조하는지 그림으로 그려보자.

참조한다

메모리 주소(refeence) 값을 가리키고 있다

관계

1번 코드

class Humam를 생성한다.
그 안에 속성(name, age)과 메소드(sleep())를 정의한다.

2번 코드

new 연산자를 사용하여 인스턴스를 생성한다.new Human('kimcoding, 30)
Human의 이름은 'kimcoding', 나이는 30살.

2번 코드를 작성함과 동시에(new Human('kimcoding', 30), __proto__가 자동으로 생성된다.

Human.prototype

new 연산자를 사용하여 인스턴스(kimcoding)를 호출할 때,
속성메소드를 전달해줄 수 있는 객체

class 내부에 constructor 내부의 속성과 하단의 메소드가 적혀있긴 하지만, Human.prototype 내부에 있다고 보는 것이 맞다.

constructor

속성을 인스턴스로 전달해주는 일련의 코드

메소드

인스턴스에는 메소드 관련 정보가 담겨있지 않다.
따라서, __proto__prototype chaining을 통해서 해당 메소드에 접근할 수 있음
즉, 메소드 관련 정보가 담겨있음

__proto__(kimcoding.__proto__)

dunder proto

new 연산자를 사용하여 인스턴스(kidmcoding)를 호출할 때, 자동으로 생기는 '객체'

참조 자료형인 객체(kimcoding.__proto__)는 prototype chaining을 통해서
메소드 sleep을 찾기 위해 Human.prototype을 참조한다.

따라서, 인스턴스에는 메소드 관련 정보가 담겨있지 않지만메소드 sleep()을 호출할 수 있다.

Human.prototype === kimcoding.proto;

kimcoding__proto__객체를 거쳐서 Human.prototype을 참조한다.

결과

따라서 이 코드는 true가 된다.

Human.prototype.sleep === kimcoding.sleep;

kimcoding 인스턴스에 메소드(sleep())이 없기 때문에,
__proto__객체를 거쳐서 이 주소가 Human.prototype으로 간다.
이러한 prototype.chaining을 통해 Human.prototype.sleep을 참조한다.

결과

따라서 이 코드는 true가 된다.

Human.prototype.constructor === Human;

클래스(Human)안에 prototype(Human.prototype)이라는 객체가 연결되어 있다.
이 객체 안에 constructor라는 속성(주소)를 만들었다.
이 주소(Human.prototype.constructor)는 Human을 가리킨다(참조한다).

역할

Human을 new로 호출했을 때, 속성을 인스턴스 객체로 전달하는 역할을 한다.

결과

constructor라는 속성은 Human과 같다.
따라서 이 코드는 true가 된다.

Human.constructor

결과

따라서 이 코드는 {[ native code ]}가 된다.

native code

'그런건 없다'고 생각하면 된다.

요약 ***

클래스가 인스턴스를 호출할 때, 클래스.prototype을 거쳐서 속성을 전달해준다.
반드시 new 연산자를 사용해야 한다.
이때 호출된 인스턴스에는 메소드 정보가 담겨있지 않기 때문에,
인스턴스는 호출 시 자동 생성된 __proto__을 거쳐서 '클래스.prototype'을 참조하여 메소드를 호출할 수 있다. 이때, '클래스.prototype'은 '클래스.prototype.constructor'를 통해 클래스를 참조한다.

코드

// 1번 코드
class Human {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

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

// 2번 코드
let kimcoding = new Human('김코딩', 30);

// 실습해보세요

// 실습1
Human.prototype.constructor === Human; // 결과는 무엇일까요? true
// 실습2
Human.prototype === kimcoding.__proto__; // 결과는 무엇일까요? true
// 실습3
Human.prototype.sleep === kimcoding.sleep; // 결과는 무엇일까요? true

좋은 웹페이지 즐겨찾기