Web/[JS] Common

프로토타입

ihl 2021. 1. 14. 17:36

  JavaScript는 기본적으로 Class라는 개념이 없다.(ES2015부터 추가되었으나 문법적 양념일 뿐 JavaScript여전히 프로토타입 기반의 언어으로 클래스 기반으로 바뀌었다는것은 아니다.) 그 대신 프로토타입을사용하여 상속을 구현하고 있다. 이 때 프로토타입은 prototype이라는 property인 prototype object와 자신을 만들어낸 객체의 원형을 가리키는 __proto__이라는 숨은 링크라는 2가지 의미를 갖고있다.

 

1. 함수의 객체선언: this vs prototype

  함수를 선언하면 함수에 생성자 자격이 부여되어 new 키워드를 사용하여 해당 함수의 형식을 사용하는 객체를 만들 수 있다. 그 예시는 다음과 같다.

function Circle(radius){
  this.radius = radius;
  this.pi = 3.14;
}
let c1 = new Circle(2);
let c2 = new Circle(5);

  c1의 radius와 pi, c2의 radius와 pi는 각각의 객체에 따로 선언된다. 즉, 모든 속성이 다른 메모리에 저장되어 총 4개의 값이 할당된다. 그러나 pi의 경우 모든 Circle 객체가 동일한 값을 가지고 있으므로 모든 Circle 객체의 pi는 같은 메모리를 참조하는 것이 더 합리적인 구현 방법이다. 이는 다음과 같이 구현할 수 있다.

 

function Circle(radius){
  this.radius = radius;
}
Circle.prototype.pi = 3.14;

let c1 = new Circle(2);
let c2 = new Circle(5);

  Circle의 prototype에 pi를 추가하면 모든 Circle 객체는 Circle.prototype.pi를 공유하게되며 이를 각 객체에서 속성으로 접근할 수 있다. Circle 함수의 prototype 속성 값인 어떤 객체의 pi 값을 Circle 객체 모두가 접근할 수 있기 때문이다. 이러한 일이 가능한 이유는 객체를 생성할 때 해당 함수의 prototype 속성 값인 객체를 Cloning하여 새로운 객체를 생성하기 때문이다. 이때 함수의 prototype 속성 값인 일종의 객체 원형이 바로 프로토타입 객체(prototype object)이며 prototype의 첫 번째 의미이다.

 

2. 함수 prototype과 객체

  c1과 Circle.prototype은 구체적으로 어떻게 연결이 되어있을까? Circle.prototype과 c1의 구조는 다음과 같다.

Circle.prototype은 c1.__proto__와 같다.

  c1의 __proto__라는 속성은 Circle.prototype를 가리킨다. (Circle.prototype === c1.__proto__로 둘을 비교해보면 결과는 true이다.)  즉, c1의 __proto__은 c1을 만들 때 사용된 객체원형(객체 생성 시 조상이었던 함수의 prototype object)에 대한 숨겨진 연결이다. 이 것이 prototype의 두 번째 의미이다.

 

  c1의 구조를 살펴보면 c1 자체에는 pi 속성이 없는 것을 알 수 있다. 대신 c1의 객체원형에 pi가 존재한다. 즉, c1.pi를 수행하면 c1에 pi가 있는지 찾아보고 없으면 __proto__를 타고 c1의 원형 객체에 가서 pi가 있는지 탐색하여 결과를 돌려주는 것이다.

 

3. 함수와 함수의 prototype object

Circle 함수

  이번엔 Circle 함수를 자세히 살펴보자. Circle.prototype.constructor가 Circle함수를 가리키고 있다. 그리고 c1의 원형객체는 Circle.prototype이다. 따라서 Circle.prototype을 Cloning하여 만들어진 c1 객체, c2 객체의 생성자 또한 Circle 함수이다. 지금까지의 내용을 정리하면 다음과 같다.

함수와 객체의 관계

  new 키워드를 통해 만들어진 Circle 객체인 c1, c2는 Circle의 prototype 속성을 cloning하여 만들어지며 Circle의 prototype 속성과 __proto__라는 링크로 연결되어있다. 즉, 함수의 prototype 속성은 하위 객체들에게 상속시킬 연결에 대한 속성이며, 객체의 __proto__는 상위에서 상속받은 prototype에 대한 정보인 것이다.

 

3. 프로토타입 체인

  프로토타입 체인이란 위의 Circle과 c1처럼 프로토타입을 통해 상위 프로토타입으로 연속해서 이어지는 관계이다.  Circle과 c1의 구조를 다시 살펴보자.

Circle함수와 c1

  c1의 원형객체 즉, Circle.prototype의 __proto__가 Object를 가리키는 것을 알 수 있다. c1의 __proto__를 타고 올라가면 가장 상위 객체인 Object 객체의 prototype까지 도달할 수 있는 것이다. 따라서 우리는 따로 toString()과 같은 메소드를 하위 객체에서 선언하지 않아도 __proto__를 통해 상위 객체로 타고 올라가 Object의 toString()에 접근할 수 있는 것이다. 이러한 프로토타입 체인의 종단은 Object의 prototype이다.

 

4. 다른 타입 변수와 객체

  모든 객체가 toString()을 사용할 수 있는 것처럼 모든 string은 concat()등의 함수를, array는 includes 등의 함수를 사용할 수 있다. 이 또한 프로토타입 체인과 관련이 있다. string 타입의 원형객체는 다음과 같다.

string의 원형 객체

  string의 원형객체는 String 함수의 prototype 속성이며 concat() 등의 함수가 포함되어있다. 이는 window객체가 선언하지 않았지만 전역으로 존재하는 것처럼 String, Number, Array 등의 함수가 이미 전역으로 선언되어있으며, string 타입 변수는 String.prototype을 상속받고있는 것이다. 다음처럼 미리 선언된 String 함수를 확인할 수 있다.

String함수

5. 재정의

  Circle의 prototype이 가지고 있는 pi를 하위 객체에서 수정하면 어떻게 될까? 다음과 같은 코드를 살펴보자

c1.pi = 3.15
c2.pi  //3.14

  pi는 prototype을 통해 공유되고 있기 때문에 c1.pi = 3.15를 수행하면 c2.pi도 변경될 것이라 생각할 수 있지만 그렇지 않다. c1의 구조를 살펴보자.

c1

  c1.pi = 3.15라는 코드를 수행하면 prototype에 있는 pi를 수정하는 것이 아니다. c1에 pi라는 속성을 따로 추가해주는 것이다. prototype을 변경하려면 반드시 Circle.prototype.pi = 3.15 혹은 c1.__proto__.pi = 3.15 라는 방식으로 접근해주어야 prototype이 수정된다.

 

 

 


medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67

 

[Javascript ] 프로토타입 이해하기

자바스크립트는 프로토타입 기반 언어라고 불립니다. 자바스크립트 개발을 하면 빠질 수 없는 것이 프로토타입인데요. 프로토타입이 거의 자바스크립트 그 자체이기때문에 이해하는 것이 어렵

medium.com

Javascript 기초 - Object prototype 이해하기 | Insanehong's Incorrect Note

 

Javascript 기초 - Object prototype 이해하기 | Insanehong's Incorrect Note

소개 이번 글에서 다룰 내용은 자바스크립트의 프로토타입 상속(prototypal inheritance) 이라는 확장과 객체의 재사용을 가능하게 해주며 class 기반으로 인스턴스를 생성하지 않는 자바스크립트에서

insanehong.kr