프로토타입(prototype)이란 한글로 원래의 형태 또는 전형적인 예라는 뜻을 가지고 있습니다.
javascript는 프로토타입 기반의 언어입니다.
그래서 javascript에서 프로토타입 개념을 이해하는 것은 매우 중요합니다.
클래스 기반언어 에서는 클래스를 먼저 선언하고 새로운 객체(인스턴스)를 생성합니다.
하지만 javascript에서는 프로토타입(prototpe)을 원형으로 삼고 이를 이용하여 객체를 생성합니다.
var jade = {
name: 'jade'
}
jade.toString() // '[object Object]'
function Person(name) {
this.name = name
}
var wade = new Person('wade')
wade.name // 'wade'
wade.hasOwnProperty('name') // true
위의 코드는 javascript에서 객체를 선언한 예시입니다.
하나는 객체 리터럴 방식을 사용하였고 하나는 생성자 함수를 사용하였습니다.
jade 변수와 wade 변수에서는 각각 toString, hasOwnProperty 메서드를 선언해주지 않았습니다.
하지만 사용 결과는 에러를 반환하지 않고 정상적인 결과를 반환하였습니다.
왜 이런 동작이 가능한 걸까요??
javascript는 프로토타입 기반의 언어이고 프로토타입 체인이 이루어져 해당 메소드들을 사용할 수 있었기 때문입니다.
javascript에서 변수는 호출된 프로퍼티가 존재하지 않으면 자신의 프로토타입 객체에 접근하여 해당 메서드가 있는지 탐색합니다.
있으면 사용, 없으면 다음 프로토타입 객체에 접근하여 메서드를 탐색 및 사용하게 됩니다.
이것을 프로토타입 체인이라 합니다.
여기서 나오는 프로토타입 객체는 자신의 원형이 되는 객체, 부모 객체라 이해하였습니다.
위 그림을 이해하면 프로토타입의 동작 흐름을 이해하는데 큰 도움이 될 수 있습니다.
위 그림과 아래의 코드를 같이 살펴보겠습니다.
function Person(name) {
this.name = name
}
var logan = new Person('logan')
console.log(Person.prototype) // { constructor: f }
console.log(Person.prototype.constructor) // f Person
console.log(logan.constructor) // f Person
위의 코드를 더 위의 그림에 도식화한 내용입니다.
이제 상세히 설명하겠습니다.
Person은 생성자 함수 입니다(javascript에서는 보통 파스칼 케이스로 선언)
logan은 new Person을 통해 생성된 객체 입니다.
Person 생성자 함수에서 prototype 속성을 통해 Person의 원형 객체에 접근할 수 있습니다.
logan에서는 __proto__를 통해 자신의 원형객체에 접근할 수 있습니다.
constructor는 새롭게 생성된 인스턴스에서 자신의 생성자 함수에 접근할 수 있는 수단입니다.
아직 한가지 동작은 설명이 되지 않았습니다.
logan.constructor의 동작입니다.
logan에는 constructor 속성이 존재하지 않습니다. 따라서 동작을 하지 않아야만 할것 같습니다.
하지만 동작합니다.
logan.__proto__.constructor이 호출되었기 때문입니다. 이때 프로토타입 체인이 등장하게 됩니다.
우선 logan 객체에서 constructor가 존재하는지 찾습니다.
없는 것을 확인했고 __proto__를 통해 logan의 프로토타입에 접근하여 constructor를 찾았고 이를 호출했습니다.
아래의 코드를 보겠습니다.
function Person(name){
this.name = name
}
var logan = new Person('logan')
console.log(logan) // { name: 'logan' }
console.log(logan.hasOwnProperty('name')) // true
console.log(logan.hasOwnProperty('constructor')) // false
console.log(logan.__proto__.hasOwnProperty('constructor')) // true
console.log(Object.getPrototypeOf(logan).hasOwnProperty('constructor')) // true
console.log(logan.__proto__.__proto__.hasOwnProperty('hasOwnProperty')) // true
console.log(
Object.getPrototypeOf(
Object.getPrototypeOf(
logan
)
).hasOwnProperty('constructor')
) // true
logan 객체에는 hasOwnProperty 메소드가 없습니다.
하지만 위의 코드는 정상적으로 동작합니다.
왜 그런것일까요??
hasOwnProperty가 호출되면 logan의 프로토타입들을 프로토타입 체인을 통해 계속 접근하였고
최상위 프로토타입인 Object에 hasOwnProperty가 존재하여 이를 호출하여 사용한 것이기 때문입니다.
아래의 그림에 조금의 설명이 있습니다.
__proto__는 객체 자신의 프로토타입 객체(부모 객체)를 찾아가는 메서드 입니다.
__proto__를 호출하게 되면 내부적으로 Object.getPrototypeOf가 호출되는 것으로 알고 있습니다.
이를 이용해 프로토타입 체인이 일어나게 됩니다.
프로퍼티가 호출되면 자신에게 존재하지 않으면 상위 프로토타입에서 해당 프로퍼티를 호출합니다.
위의 코드에서 Object.hasOwnProperty가 logan.hasOwnProperty가 호출된 사례 입니다.
이 왼쪽트리가 갑자기 등장해서 이해가 안되었을 수도 있습니다.
javascript에서 모든 Wrappe 함수의 결과물은 객체 입니다.
Function도 객체, String도 객체, Number도 객체, Boolean도 객체 입니다.
이런 프로토타입의 속성을 이용해서 클래스 기반에서 동작하는 상속을 구현할 수 있습니다.
function Person(name) {
this.name = name
this.getName = function() {
console.log(this.name)
}
}
var logan = new Person('logan')
logan.getName()
이런식으로 메소드를 생성자 함수 내에서 정의해주면 새로 생성된 객체는 getName 메소드를 호출하게 됩니다.
이 생성자 함수를 이용하여 무수히 많은 객체를 생성한다고 가정하겠습니다.
그러면 객체마다 getName이라는 메서드에 메모리 영역을 할당해야 합니다.
이러한 메모리 영역을 프로토타입을 이용하면 세이브 할 수 있습니다
function Person(name) {
this.name = name
}
Person.prototype.getName = function () {
console.log(this.name)
}
var logan = new Person('logan')
logan.getName()
위와 같이 프로토타입 체인을 이용하면 Person 생성자 함수가 무수히 많은 객체를 생성해도
생성된 객체 내에 getName 메소드가 존재하지 않으므로 __proto__를 통해 getName메소드에 존재합니다.
그러면 메모리 영역을 객체마다 할당하지 않아도 같은 결과를 가져올 수 있습니다.
프로토 타입 체인 -> 프로퍼티를 가져오기 위해 자신의 프로토타입에 프로퍼티가 있는지 찾기위한 과정
자식이 차키가 없으면 부모의 차키를 찾고 그 다음엔 조부모의 차키를 찾는다.
차키를 찾지 못하면 그 차는 사용할수가 없다.
스코프 체인 -> 실행컨텍스트에서 변수를 찾기 위해 스코프체인 프로퍼티를 참조하여 변수를 찾기위한 과정
출처: https://poiemaweb.com/js-prototype
https://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85
* 코어자바스크립트 책 6장 프로토타입
'Frontend > Javascript' 카테고리의 다른 글
Javascript 콜백 📞 (0) | 2022.04.19 |
---|---|
Javascript Promise, async/await MicroTask queue 동작차이 🖇 (1) | 2022.04.15 |
React-query 🌱 (1) | 2022.04.09 |
Javascript 실행 컨텍스트(Execution Context) (0) | 2022.04.03 |
npm, yarn (0) | 2022.04.03 |