javascript - prototype

김형석·2022년 8월 8일
0

개념공부 스터디

목록 보기
24/27

JavaScript의 Prototype

자바스크립트는 명령형, 함수형, 프로토타입 기반 객체지향 프로그래밍을 지원하는 멀티 패러다임 프로그래밍 언어이다.
프로토타입 기반 언어는 객체 원형인 프로토타입을 이용하여 새로운 객체를 만들어 낸다. 프로토타입은 객체를 확장하고 객체 지향적인 프로그래밍을 할 수 있게 해준다.

상속(inheritance)과 프로토타입

상속은 객체지향 프로그래밍의 핵심 개념으로, 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 말한다.

자바스크립트는 프로토타입을 기반으로 상속을 구현하여 불필요한 중복을 제거한다. 중복을 제거하는 방법은 기존의 코드를 적극적으로 재사용하는 것이다. 코드 재사용은 개발 비용을 현저히 줄일 수 있는 잠재력이 있으므로 매우 중요하다.

Prototype ?

클래스 기반 객체지향 프로그래밍 언어는 객체 생성 이전에 클래스를 정의하고 이를 통해 객체(인스턴스)를 생성한다. 하지만 프로토타입 기반 객체지향 프로그래밍 언어는 클래스 없이도 객체를 생성할 수 있다.

자바스크립트의 객체 생성 방법
1.리터럴표기를 이용한 방법
2.생성자 함수를 이용한 방법
3.Object.create() 메서드를이용한 방법

자바스크립트의 모든 객체는 자신의 부모 역할을 담당하는 객체와 연결되어 있다. 그리고 이것을 마치 객체 지향의 상속 개념과 같이 부모 객체의 프로퍼티 또는 메서드를 상속받아 사용할 수 있게 한다.

Prototype은 클래스, 객체의 내용 복사 없이도 상속을 구현할 수 있게 해주는 방법이다.
상속의 구체적인 수단이자 상속을 구현할 수 있는 또 하나의 문법이다.

function 생성자(){
  this.name = 'Kim';
  this.age = 15;
}
var 인스턴스1 = new 생성자();
var 인스턴스2 = new 생성자();

console.log(생성자.prototype);

왜 사용?

Prototype 객체는 생성자 함수에 의해 생성된 각각의 객체에 공유 프로퍼티를 제공하기 위해 사용한다.

//생성자 함수
function Circle(radius) {
  this.radius = radius;
  this.getArea = function() {
    return Math.PI * this.radius **2;
  };
}
  
//반지름이 1인 인스턴스 생성
const circle1 = new Circle(1);
//반지름이 2인 인스턴스 생성
const circle1 = new Circle(2);

//Circle 생성자 함수는 인스턴스를 생성할 때마다 동일한 동작을 하는
//getArea 메서드를 중복 생성하고 모른 인스턴스가 중복 소유한다.
//getAtra 메서드는 하나만 생성하여 모든 인스턴스가 공유해서 사용하는 것이 바람직하다.
console.log(circle1.geaArea === circle2.getArea); //false

console.log(circle1.geaArea()); //3.1415...
console.log(circle2.geaArea()); //12.5663...

생성자 함수는 동일한 프로퍼티(메서드 포함) 구조를 갖는 객체를 여러 개 생성할 때 유용하다. 하지만 위 예제의 생성자 함수는 문제가 있다.

Circle 생성자 함수가 생성하는 모든 객체(인스턴스)는 radius 프로퍼티와 getArea 메서드를 갖는다. radius 프로퍼티 값은 일반적으로 인스턴스마다 다른다. 하지만 getArea 메서드는 모든 인스턴스가 동일한 내용의 메서드를 사용하므로 단 하나만 생성하여 모든 인스턴스가 공유해서 사용하는 것이 좋다.

그런데 Circle 생성자 함수는 인스턴스를 생성할 때마다 getArea 메서드를 중복 생성하고 모든 인스턴스가 중복 소유한다.

상속을 통해 불필요한 중복을 제거해보자. 자바스크립트는 프로토타입을 기반으로 상속을 구현한다.

// 생성자 함수
function Circle(radius) {
  this.radius = radius;
}

// Circle 생성자 함수가 생성한 모든 인스턴스가 getArea 메서드를
// 공유해서 사용할 수 있도록 프로토타입에 추가한다.
// 프로토타입은 Circle 생성자 함수의 prototype 프로퍼티에 바인딩되어 있다.
Circle.prototype.getArea = function () {
  return Math.PI * this.radius ** 2;
};

// 인스턴스 생성
const circle1 = new Circle(1);
const circle2 = new Circle(2);

// Circle 생성자 함수가 생성한 모든 인스턴스는 부모 객체의 역할을 하는
// 프로토타입 Circle.prototype으로부터 getArea 메서드를 상속받는다.
// 즉, Circle 생성자 함수가 생성하는 모든 인스턴스는 하나의 getArea 메서드를 공유한다.
console.log(circle1.getArea === circle2.getArea); // true

console.log(circle1.getArea()); // 3.141592653589793
console.log(circle2.getArea()); // 12.566370614359172

앞에서 언급했듯이 이렇게 상속은 재사용이란 관점에서 매우 유용하다. 생성자 함수가 생성할 모든 인스턴스가 공통적으로 사용할 프로퍼티나 메서드를 프로토타입에 미리 구현해 두면 생성자 함수가 생성할 모든 인스턴스는 별도의 구현 없이 상위(부모) 객체인 프로토타입의 자산을 공유하여 사용할 수 있다.

Prototype 체인

  1. circle1 에 직접 getArea() 메서드가 있는가?
  2. 그럼 부모 prototype에 getArea() 메서드가 있는가?
  3. 있으면 실행 없으면 부모의 부모의 유전자 확인
  4. 만약 다 없다면 오브젝트라는 이름의 생성자 함수에 prototype 객체에 도달했을 때 멈춘다.(undefined 반환)

이것을 프로토타입 체인이라 부르며, 그냥 쉽게말하자면 오브젝트에서 값을 뽑을 때

  1. 내가 직접 가지고 있는지 검사

  2. 내가 가지고 있지 않으면 부모 유전자들을 차례로 검사하는구나

라고 잘 기억해주시면 되겠다.

그래서 결과적으로 circle1과 circle2가 getArea()라는 메서드를가지고 있지 않지만 Circle.prototype(부모의 유전자)에 있는 getArea() 메서드를 사용할 수 있는 이유이다.

+ 내부함수는 어떻게 동작하는가

var arr = [1,2,3];
var arr = new Array(1,2,3); //실제 array가 만들어지는 방식
var obj = { name : 'kim' }
var obj = new Object()

arr 라는 변수를 배열 리터럴로 생성했지만 내부적으로는 new Array() 같이 생성자 함수로 선언한 것이다.
arr 배열 객체의 원형은 Array이다.
Array.prototype
arr.sort()
arr.map()
arr.push()...

중간 정리

  • 자바스크립트는 프로토타입 기반 언어이다.
  • 프로토타입은 원형(유전자)라는 뜻이다. (객체의 원형, 즉 객체의 부모가 가지는 유전자 즉, 상속받은 데이터, 메소드)
  • 함수 객체에는 프로토타입이라는 특수한 유형의 객체 프로퍼티(속성)가 존재한다. (객체와 프로터티가 같은 의미가 아니다.)
  • 이러한 prototype 프로퍼티를 통해 생성자 함수는 인스턴스에게 프로토타입 객체에 있는 데이터, 메소드를 상속한다. (인스턴스는 사용 가능하다.)
  • 인스턴스 객체의 key에 접근할 때, 해당 객체에게 key가 없다면 그 다음으로 상위 프로토타입(원형) 속성에서 key가 있는지 확인한다.
  • 없다면 그것을 찾기 위해 더 상위의 프로토타입(부모)에서 찾는다. 이것을 프로토타입 체인이라고 한다.

__proto__

__proto__접근자 프로퍼티
부모의 유전자(부모의 prototype)를 검사하고 싶다면 __proto__

function 생성자(){
  this.name = 'Kim';
  this.age = 15;
}
var 인스턴스1 = new 생성자();
var 인스턴스2 = new 생성자();

console.log(생성자.prototype);
console.log(인스턴스1.__proto__);

console.log(인스턴스1.__proto__ === 생성자.prototype);

profile
블로그 이사 : https://hengxi.tistory.com

0개의 댓글