My Boundary As Much As I Experienced

JS) 프로토타입(Prototype) 본문

FrontEnd/Javascript(Vanilla)

JS) 프로토타입(Prototype)

Bumang 2023. 7. 22. 11:25

자료형을 생성하는 2가지 방식

프로토타입을 만들기 전에 예시로 배열을 만드는 데는 2가지 방법을 사용해보자

const fruits = ["Apple", "Banana", "Cherry"];
//배열 리터럴 방식

const fru2 = new Array("Apple", "Banana", "Cherry");
//사실 리터럴로 입력해도 컴퓨터는 생성자 함수 방식으로 생성한다.

하나는 배열 리터럴 방식으로, []을 이용해서 직관적으로 자신이 넣고싶은 데이터들을 넣을 수 있다.

나머지 하나는 생성자 함수를 사용하는 방법이다. new 연산자와 맨 앞글자가 대문자인(파스칼 표기법) 생성자 함수를 사용하는 방식이다.

 

Array뿐만 아니라, Object도 이와 같이 생성할 수 있다. 생성자 함수(틀)에서 내가 원하는 객체, 배열(결과물)들을 찍어내는 방식이다.

배열 리터럴 방식을 사용해도 컴퓨터는 생성자 함수를 통해 복제한 결과물을 반환하는 것이었다.

 

 

const fruits = ["apple", "banana"]

console.log(fruit.length)
console.log(fruit.includes("apple"))
console.log(fruit.forEach(fruit => console.log(fruit)))
// 우리가 알고 있는 Array 자료형일 때 사용할 수 있는 메소드들. 왜 사용할 수 있는지 생각해본 적 있나?

우리가 배열을 생성하면 기본적으로 사용할 수 있는 속성 혹은 메소드들이 있었다. 이 메소드들도 어딘가에 선언되어 있기 때문에 우리가 쓸 수 있는거다. 그곳은 바로! 프로토타입이란 곳이다. 생성자 함수를 만들면 (자바스크립트는 우리가 모르게) 그에 대응하는 프로토타입이라는 객체를 만든다.

 

mdn에서 자료형 메소드를 찾으면 볼 수 있는 .prototype. 표기

이렇듯 프로토타입에 선언해서 모든 인스턴스에서 사용할 수 있는 메소드를 프로토타입 메소드라고 부른다. 그에 반해 인스턴스에선 사용할 수 없고 생성자 함수에서만 사용할 수 있는 메소드도 있는데 이를 정적 메소드라고 부른다. 당장 정적 메소드에 대해 자세하게 다루진 않겠지만, 프로토타입 메소드가 어떻게 선언되어 있는지는 알 필요가 있다.

 

 

 

생성자 함수로 프로토타입 프로퍼티 생성

function Person(first, last, height) {
    this.firstName = first
    this.lastName = last
    this.height = height
}

생성자 함수는 직접 만들어볼수도 있다. 위처럼 Person이란 새로운 생성자 함수를 만들었다고 해보자.

그러면 자바스크립트는 위와 같이 Person이란 생성자 함수와 그에 대응하는 Person의 프로토타입을 만드는 것이다.

프로토타입은 어떻게 보면 유전자라고 볼 수 있다. Person의 프로토타입은 앞으로 Person이 생성될 때 공통적으로 가지고 가야할 유전자 형질을 상속해주는 존재이다.

 

 

프로토타입에 접근하여 메소드 생성

Person.prototype.sum = function (){
	console.log(`sum ${this.first}`)
}

 우리는 prototype에 접근해서 새로운 메소드도 추가할 수 있다.

 

const kim = new Person("Josee", "Kim", 166)
const hwang = new Person("Minsu", "Hwang", 177)

kim.sum() // sum Josee
hwang.sum() // sum Minsu

앞서 추가한 메소드를 josee와 minsu안에 직접 만들지 않았어도 Person의 프로토타입을 참조하여 메소드를 찾아 실행시킬 수 있게 되는 것이다. 우리가 알던 Array와 Object 모두 prototype에 프로퍼티와 메소드들을 할당하여 기본적으로 제공하던 것이었다.

 

우리가 kim이란 인스턴스(복사본)을 Person으로부터 생성했을 때, 아래와 같은 일들이 일어난다.

인스턴스(복사본)를 생성하면 그 안에 __proto__라는 속성으로 원형의 프로토타입을 연결시켜놓는다.

kim이란 인스턴스를 만들어서 우리가 설정한 name, first, ... 같은 정보들 뿐만 아니라 __proto__라는 보지못한 속성도 추가가 된걸 볼 수 있다. __proto__는 이 인스턴스의 원형이 되는 프로토타입을 참조하도록 걸어둔 link라고 보면 된다.

 

 

우리가 메소드를 호출할 때 벌어지는 일들

  1. 호출한 객체에 해당 메소드가 있는지 찾아본다.
  2. 없다면 __proto__를 타고 prototype에 접근하여 메소드가 있는지 확인한다.
  3. 또 없다면 __proto__를 타고 상위 prototype에 접근하여 메소드가 있는지 확인한다.
  4. 계속 없다면 이렇게 쭉 이어가다가 Object의 프로토타입까지 뒤져보고, Object는 프로토타입이 null이기 때문에 여기서 탐색을 멈추고 참조에러가 뜬다.

 

__proto__란?

모든 객체는 __proto__를 통해 자신이 물려받은 [[Prototype]] 값에 접근할 수 있다.
재해석 : __proto__라는 프로퍼티로 유전자 접근이 가능하다.

 

하지만 [[Prototype]] 내부 슬롯에는 직접 접근이 불가하다. 이는 프로토타입 체인의 단방향을 지키기 위해서다. 만약 직접 접근가능하다면, 서로가 서로의 프로토타입이 되면서 프로토타입 체인이 무한으로 돈다. 따라서 __proto__ 프로퍼티로만 접근할 수 있다.

 

자바스크립트는 객체 지향 언어가 아니라 프로토타입 지향 언어였다.

다른 객체 지향 언어처럼 Class가 없지만 Prototype을 통해 원본과 인스턴스 관계를 생성할 수 있었다.(물론 메소드나 속성을 프로토타입에 지정하는 해괴한 문법을 가지고 있지만..)

그러나 ES6에 이르러 다른 자바같은 언어가 가지고 있는 class 문법을 만들었고, 현재는 class를 사용해서 객체들을 관리할 수 있다. (클래스를 문법적으로 구현해놓았지만 사실 prototype 개념을 조작하는 것이다)

 

 

참고

 

 

'FrontEnd > Javascript(Vanilla)' 카테고리의 다른 글

JS) 비동기와 Promise  (0) 2023.08.01
콜백함수  (0) 2023.08.01
JS) this란 무엇인가?  (0) 2023.07.21
JS) 화살표 함수  (0) 2023.07.21
JS) 호출 스케줄링  (0) 2023.07.20