15. 프로토타입

HoonDong_K·2022년 6월 24일
0
post-thumbnail

🔅 객체지향 프로그래밍

객체지향 프로그래밍은 객체의 집합으로 프로그램을 표현하려하는 프로그래밍 패러다임을 말한다.

실세계의 실체는 각각 고유의 속성 ( attribute / property )를 가지고 있고, 이를 통해 우리는 실체들을 인식하거나 구별할 수 있다.

이런 철학적 사고를 프로그래밍에 접목시켜, 우리가 구현하려는 객체를 필요한 속성만 간추려 프로그래밍으로 추상화할 수 있다.

// '나' 라는 사람의 필요한 속성을 추상화한 객체
const person = {
    name: 'Kang',
    address: 'Seoul',
}

이렇게 속성을 통해 여러 개의 값을 하나의 단위로 구성한 복합적 자료구조를 객체라 하며, 독립적인 객체의 집합으로 프로그램을 표현하려는 패러다임을 객체지향 프로그래밍이라 한다.

🔅 상속과 프로토타입

상속 ( inheritance )은 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 말한다.

// Circle 생성자 함수
function Circle(radius) {
    this.radius = radius
    this.getArea = function () {
        return Math.PI * radius ** 2
    }
}
//인스턴스 circle1, circle2
const circle1 = new Circle(5)
const circle2 = new Circle(10)

인스턴스는 각자 고유의 반지름을 갖고 있지만, getArea 메서드는 모든 인스턴스가 동일하게 갖고 있다. 이는 즉, 새로운 인스턴스가 생성될 때마다 동일한 메서드를 중복 생성하게 되며 메모리를 불필요하게 낭비하게 된다.

이를 해결하기 위해,
상속을 이용하여 불필요한 중복을 제거 할 수 있고
자바스크립트는 프로토타입을 기반으로 상속을 구현한다.

function Circle(radius) {
    this.radius = radius
}
// Circle.prototype 에 getArea 메서드를 추가하여
// 인스턴스들이 사용할 수 있도록 공유한다.
Circle.prototype.getArea = function () {
    return Math.PI * this.radius ** 2
}

const circle1 = new Circle(5)
console.log(circle1.getArea()) //78.53981633974483

🔅 프로토타입 객체

프로토타입 객체란 객체지향 프로그래밍의 근간을 이루는 객체 간 상속을 구현하기 위해 사용된다.
프로토타입은 어떤 객체의 상위 객체의 역할을 하는 객체로서, 다른 객체에게 공유 프로퍼티들을 제공한다.

  • 모든 객체는 [[Prototype]]이라는 내부 슬롯을 가지고 있다.
  • [[Prototype]]에 직접 접근할 수 없기에, __proto__ 접근자 프로퍼티를 통해 간접 접근할 수 있다.

생성자 함수인 Circleprototype을 통해 Circle.prototype에 접근할 수 있고,
Circle.prototypeconstructor을 통해 Circle에 접근할 수 있다.

❗ __proto__ 접근자 프로퍼티

  1. __proto__ 는 접근자 프로퍼티이다.

접근자 프로퍼티는 값을 갖고 있지 않고, 다른 데이터 프로퍼퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수이다.

  • __proto__을 통해 프로토타입에 접근하면 get __proto__ 함수로 값을 가져오고
  • __proto__을 통해 프로토타입에 접근해 새로운 프로토타입을 할당하면 set __proto__이 호출된다.
const obj = {}
const parent = { x: 1 }

console.log(obj.__proto__) // get __proto__
obj.__proto__ = parent // set __proto__
console.log(obj) // {}
console.log(obj.x) // 1
  1. __proto__ 는 상속을 통해 사용된다.

__proto__는 객체가 직접 소유하는 프로퍼티가 아니라 Object.prototype의 프로퍼티이고,
상속을 통해 모든 객체가 Object.prototype.__proto__ 접근자 프로퍼티를 사용할 수 있다.

  1. __proto__ 를 통해 프로토타입에 접근하는 이유
const parent = {}
const child = {}

parent.__proto__ = child
child.__proto__ = parent //Uncaught TypeError: Cyclic __proto__ value

상호참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위해서이다. 프로토타입 체인은 단방향으로 이루어지기 때문에 순환 참조하는 프로토타입 체인이 만들어지면 종점이 존재하지 않아 프로퍼티 검색을 무한하게 진행된다.

그렇기 때문에 무조건적으로 프로토타입을 교체하지 못하도록 __proto__를 통해 접근하여 교체하도록 구현되었다.

  1. __proto__ 를 코드 내에 직접 사용하는 것은 권장하지 않는다.

__proto__는 프로토타입 체인의 종점인 Object.prototype의 프로퍼티이지만, 이 위에 또 다른 프로토타입이 생성된다면 Object.prototype 이상의 객체에는 접근할 수 없다.

=>Object.getPrototypeOfObject.setPrototypeOf 메서드를 이용한다.

const obj = {}
const parent = { x: 1 }

Object.getPrototypeOf(obj)
Object.setPrototypeOf(obj, parent)

❗ 함수 객체의 prototype 프로퍼티

함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다.

  • 생성자 함수가 아닌 ( non-constructor ) 화살표 함수와 ES6 메서드 축약 표현으로 정의한 메서드는 prototype 프로퍼티를 소유하지 않으며 프로토타입도 생성하지 않는다.
구분소유사용주체사용목적
__proto__모든 객체프로토타입 참조모든 객체객체가 자신의 프로토타입에 접근 또는 교체하기 위해 사용
prototypeconstructor프로토타입 참조생성자 함수생성자 함수가 생성할 객체(인스턴스)의 프로토타입을 할당하기 위해 사용

❗ constructor 프로퍼티와 생성자 함수

constructor프로퍼티는 자신을 참조하고 있는 생성자 함수를 가리킨다.

console.log(circle1.constructor)
/*
ƒ Circle(radius) {
    this.radius = radius
}
/*

circle1 객체는 constructor프로퍼티를 갖고 있지 않지만 Circle.prototype을 상속하고 있으니 프로토타입의 메서드와 프로퍼티를 사용할 수 있다.

🔅 리터럴 객체의 생성자 함수와 프로토타입

지금까지 살펴본 객체는 생성자 함수로 생성되었지만 객체는 생성자 함수 뿐만 아니라 리터럴 표기법에 의해 생성될 수 있다. 그렇다면 리터럴 표기법에 의해 생성된 객체는 프로토 타입이 어떻게 구성되어 있을까

const person = {
    name: 'Kang',
    address: 'Seoul',
}

console.log(person.constructor)
//ƒ Object() { [native code] }

리터럴 표기법으로 생성한 객체는 Object 생성자 함수를 constructor로 인식하고 있다.

Object 생성자 함수로 생성한 객체와 객체 리터럴로 생성한 객체는 생성 과정에서 미묘한 차이를 갖고 있지만 결국 객체러서 동일한 특성을 갖고 있다.

  • 객체 리터럴 생성자 함수 : Object - Object.prototype
  • 함수 리터럴 생성자 함수 : Function - Function.prototype
  • 배열 리터럴 생성자 함수 : Array - Array.prototype
  • 정규 표현식 리터럴 생성자 함수 : RegExp - RegExp.prototype

🔅 프로토타입 생성 시점

  1. 사용자 정의 생성자 함수와 프로토타입 생성 시점
  • 생성자 함수로서 호출할 수 있는 함수 객체를 생성하는 시점에 프로토타입도 생성된다.
  • non - constructor 함수( 생성자 함수가 아닌) 는 프로토타입이 생성되지 않는다.

  1. 빌트인 생성자 함수와 프로토타입 생성 시점
  • Object, String, Number 등 빌트인 생성자 함수가 생성되는 시점에 프로토타입이 생성된다.
  • 빌트인 생성자 함수는 전역 객체가 생성되는 시점에 생성된다.
  • 전역 객체는 코드가 실행되기 이전 단계에 생성되는 특수한 객체이다. (widown / global)

🔅 프로토타입 체인

자바스크립트는 객체의 프로퍼티에 접근하려 할 때, 해당 객체에 프로퍼티가 없다면 프로토타입 체인을 따라서 상위 체인으로 순차적으로 검색한다.

function Person(name) {
    this.name = name
}

Person.prototype.sayHello = function () {
    console.log(`Hi! my name is ${this.name}`)
}
// Person 생성자 함수에 생성된 me 인스턴스
const me = new Person('Kang')

만약 me 에서 프로퍼티를 찾다가 존재하지 않으면 Person.prototype으로 넘어가고 여기도 존재하지 않으면 체인에 따라 이동하며 검색하는 메커니즘이다.

프로토타입 체인의 종점은 Object.prototype이며 Object.prototype.__proto__ 는 null을 갖는다.

프로토타입의 체인은 상속과 프로퍼티 검색을 위한 메커니즘이라고 할 수 있다.

profile
코드 한 줄이 나를 증명하는 개발자

0개의 댓글