HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🤩
개발
/
NodeJs
NodeJs
/
Javascript
Javascript
/
1️⃣
프로토타입
1️⃣

프로토타입

프로토타입 기반 언어프로토타입 이해하기생성자 속성프로토타입 수정하기메서드속성프로토타입 체인일반적인 방식 : 속성은 생성자에서, 메소드는 프로토타입에서 정의프로토타입 기반 작성 🆚 클래스 기반 작성
[ 참조 공식 문서 ]

프로토타입 기반 언어

  • JavaScript는 흔히 프로토타입 기반 언어라 불림
  • 모든 객체들이 메소드와 속성들을 상속받기 위한 템플릿으로써 프로토타입 객체를 가진다는 의미임
  • 프로토타입 객체도 또 다시 상위 프로토타입 객체로부터 메소드와 속성을 상속 받을 수도 있고, 그 상위 프로토타입 객체도 마찬가지 ⇒ 이것이 프로토타입 체인 ( 다른 객체에 정의된 메소드와 속성을 한 객체에서 사용할 수 있도록 하는 근간)

프로토타입 이해하기

  • 상속되는 속성과 메소드들은 각 객체가 아닌, 객체의 생성자의 prototype 이라는 속성에 정의되어 있음
    • JavaScript에서는 객체 인스턴스와 프로토타입 간에 연결이 구성되며 이 연결을 따라 프로토타입 체인을 타고 올라가며 속성과 메소드를 탐색함
      var Person = function (name) { this._name = name; }; Person.prototype.getName = function() { return this._name; }
      💡
      자바스크립트는 함수에 자동으로 객체인 prototype 프로퍼티를 생성해 놓는데, 해당 함수를 생성자 함수로서 사용할 경우(new 연산자와 함께 함수를 호출할 경우), 그로부터 생성된 인스턴스에는 숨겨진 프로퍼티인 __proto__ 가 자동으로 생성되며, 이 프로퍼티는 생성자 함수의 prototype 프로퍼티를 참조함 __proto__ 프로퍼티는 생략 가능하도록 구현돼 있기 때문에 생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 마치 자신의 것처럼 해당 메서드나 프로퍼티에 접근할 수 있음
      [ String mdn 문서 ] - 여기서 정적 메서드, 인스턴스 속성, 인스턴스 메서드 확인하면 무슨 말인지 이해됨
Object에 정의되어 있는 메소드를 person1 에서 호출하면 일어나는 일은 아래 과정
function Person(first, last, age, gender, interests) { // 속성과 메소드 정의 this.first = first; this.last = last; //... } var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']); person1.valueOf()
  1. 브라우저는 우선 person1 객체가 valueOf() 메소드를 가지고 있는지 체크함
  1. 없으므로 person1의 프로토타입 객체(Person() 생성자의 프로토타입)에 valueOf() 메소드가 있는지 체크
  1. 여전히 없으므로 Person() 생성자의 프로토타입 객체의 프로토타입 객체(Object() 생성자의 프로토타입)가 valueOf() 메소드를 가지고 있는지 체크합니다. 여기에 있으니 호출하며 끝납니다!
특정 객체의 프로토타입 객체에 바로 접근하는 방법( __proto__ )
  • instance.__proto__
    • 뒤에 __proto__ 를 하나 더 붙이면 그 상위 프로토타입 객체를 반환함
  • 그러나 __proto__ 는 생략가능하며, instance.method() 형태로 호출이 가능함
  • Object.getPrototypeOf(instance);

생성자 속성

  • 모든 생성자 함수는 constructor 속성을 지닌 객체를 프로토타입 객체로 가지고 있음
  • constructor 속성은 원본 생성자 함수 자신을 가리킴
var person3 = new person1.constructor('Karen', 'Stephenson', 26, 'female', ['playing drums', 'mountain climbing']);
  • 실행 도중 명시적인 생성자 함수를 예측할 수 없는 상황에서 인스턴스를 생성해야 하거나 하는 경우 유용하게 사용할 수 있는 방법임

프로토타입 수정하기

메서드

function Person(first, last, age, gender, interests) { // 속성과 메소드 정의 } var person1 = new Person('Tammi', 'Smith', 32, 'neutral', ['music', 'skiing', 'kickboxing']); Person.prototype.farewell = function() { alert(this.name.first + ' has left the building. Bye for now!'); };
  • person1을 만들고나서 prototype에 메소드를 업데이트 하더라도 바로 사용이 가능함
    • 프로토타입 객체는 모든 인스턴스에서 공유하기 때문에

속성

  • prototype에 속성을 정의하는 경우는 별로 좋지 않은 방법임
Person.prototype.fullName = this.name.first + ' ' + this.name.last;
  • 이 경우 this는 함수 범위가 아닌 전역 범위를 가리키므로 코드가 의도대로 동작하지 않습니다.
  • 이대로 실행해도 undefined undefined만 볼 수 있죠.
  • 윗 절에서 프로토타입에 정의한 메소드 내에서는 정상적으로 동작하는데 이는 코드가 함수 범위 내에 있으며 객체의 멤버 함수로써 동작하기에 객체 범위로 전환되었기 때문입니다
  • 따라서 프로토타입에 상수(한 번 할당하면 변하지 않는 값)를 정의하는 것은 가능하지만 일반적으로 생성자에서 정의하는 것이 낫습니다

프로토타입 체인

// o라는 객체가 있고, 속성 'a' 와 'b'를 갖고 있다고 하자. let f = function () { this.a = 1; this.b = 2; } let o = new f(); // {a: 1, b: 2} // f 함수의 prototype 속성 값들을 추가 하자. f.prototype.b = 3; f.prototype.c = 4; // f.prototype = {b: 3, c: 4}; 라고 하지 마라, 해당 코드는 prototype chain 을 망가뜨린다. // o.[[Prototype]]은 속성 'b'와 'c'를 가지고 있다. // o.[[Prototype]].[[Prototype]] 은 Object.prototype 이다. // 마지막으로 o.[[Prototype]].[[Prototype]].[[Prototype]]은 null이다. // null은 프로토타입의 종단을 말하며 정의에 의해서 추가 [[Prototype]]은 없다. // {a: 1, b: 2} ---> {b: 3, c: 4} ---> Object.prototype ---> null
Arraty의 내부를 살펴보면 [[Prototype]] 안에 또 [[Prototype]] 이 있는 것을 확인할 수 있음
Arraty의 내부를 살펴보면 [[Prototype]] 안에 또 [[Prototype]] 이 있는 것을 확인할 수 있음
notion image
  • 어떤 데이터의 [[Prototype]] 내부에 다시 [[Prototype]]가 연쇄적으로 이어진 것을 프로토타입 체인이라 하고, 이 체인을 따라가며 검색하는 것을 프로토타입 체이닝이라고 함

일반적인 방식 : 속성은 생성자에서, 메소드는 프로토타입에서 정의

 

프로토타입 기반 작성 🆚 클래스 기반 작성

function Logger(name) { this.name = name; } Logger.prototype.log = function(message) { console.log(`[${this.name}] ${message}`); } Logger.prototype.info = function(message) { this.log(`info: ${message}`); }; Logger.prototype.verbose = function(message) { this.log(`verbose: ${message}`); }; module.exports = Logger;
class Logger { constructor(name) { this.name = name; } log(message) { console.log(`[${this.name}] ${message}`); } info(message) { this.log(`info: ${message}`); } verbose(message) { this.log(`verbose: ${message}`); } } module.exports = Logger;