본문 바로가기
개발 공부 일지/코어 자바스크립트 (정재남)

[코어 자바스크립트] 프로토타입

by yelimu 2025. 3. 10.

프로토타입

마지막 챕터인줄 알았는데 아니었다 ㅎ ㅎ

자바스크립트는 프로토타입 기반 언어라고 하는데, 그동안 개발 공부하면서 이에 대해 들을 기회가 딱히 없었다. 

https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain

 

상속과 프로토타입 - JavaScript | MDN

JavaScript는 동적 타입이고 정적 타입이 없기 때문에, (Java 또는 C++와 같은) 클래스 기반 언어에 경험이 있는 개발자에게는 약간 혼란스럽습니다.

developer.mozilla.org

MDN에서 클래스 기반 언어 경험이 있는 개발자는 혼란스러울수도 있다고 하는데 

나는 처음이니 혼란스러울게 없어서 럭키 비키 잖아? 🍀


어떤 생성자 함수를 new 연산자와 함께 호출하면 Constructor 에서 정의된 내용을 바탕으로 새로운 인스턴스가 생성되는데 
이 인스턴스에는 `__proto__` 라는 프로퍼티가 자동으로 부여된다. 
이 프로퍼티는 Constructor 의 prototype 프로퍼티를 참조한다. 

인스턴스가 자신의 생성자 함수가 무엇인지를 알고자 할때 필요한 수단이다. 

 

생성자 함수
생성자 함수(constructor function)와 일반 함수에 기술적인 차이는 없습니다. 다만 생성자 함수는 아래 두 관례를 따릅니다.
함수 이름의 첫 글자는 대문자로 시작합니다.반드시 'new' 연산자를 붙여 실행합니다.

https://ko.javascript.info/constructor-new

이미지 자료가 안보여서 캔바에서 그려옴

var Person = function (name){
	thils._name = name;
}

Person.prototype.getName = function(){
	return this._name;
}

var suzi = new Person("suzi");
suzi.__proto__.getName(); // (1) undefined

Proson.prototype === suzi.__proto__ // true;

suzi.__proto__._name = "SUZI__proto__"; // _name 프로퍼티를 부여하고 나면
suzi.__proto__.getName() // (2) SUZI__proto__ 출력됨

suzi.getName(); // (3) suzi 출력됨 : __proto__ 생략 가능하므로

수지 좋아하시나보다

suzi 라는 인스턴스가 생성될때 __proto__ 은 Person.prototype 객체를 동일하게 참조한다.

 

(1)번 코드에서 getName () 메서드를 호출한 대상이 suzi.__proto__ 가 this 로 바인딩 되기 때문에 

(2)번 코드 출력전에 suzi.__proto__ 의 _name 프로퍼티에 값을 부여하고나서야 getName() 에 의해 값이 출력이 된다.

 

(3) getName() 메서드가 suzi에 의해 바로 호출되면 

이때는  this = suzi 이므로 "suzi"가 출력이 된다. 

__proto__ 는 생략이 가능하게 설계 되었기때문에 이렇게 작동한다고 이해하면 된다고한다. ^.^ 

 

다시한번 이 그림을 문장으로 설명하면

어떤 생성자 함수를 new 연산자와 함께 호출하면 Constructor 에서 정의된 내용을 바탕으로 새로운 인스턴스가 생성되는데 
이 인스턴스에는 `__proto__` 라는 프로퍼티가 자동으로 부여된다.
이 프로퍼티는 Constructor 의 prototype 프로퍼티를 참조한다. 

 

또 다른 예시를 보면 

let arr = [1,2]
console.dir(arr)
console.dir(Array)

 

console.dir([1,2])

 

console.dir(Array)

Array 는 생성자 함수이고 

arr [1,2] 는 Array 의 인스턴스 이다. 

 

arr [1, 2] 의 __proto__ 에 있는 객체가 Array의 prototype 와 동일한 것을 볼 수 있다. 

즉 인스턴스의 __proto__ 는 contruncor.prototype 을 참조한다. 

 

__proto__ 가 생략가능하도록 설계되어있기때문에 인스턴스가 push, pop, forEach 등의 메서드를 마치 자신의 것처럼 호출할 수 있다. 

Array.prototype 프로퍼티 내부에 있지 않은 fill, isArray 와 같은 메서드는 인스턴스가 직접 호출할 수 없다. 

Array.isArray(arr) // true
arr.isArray(); // 타입에러 - 호출 불가

 

왜 어떤 메서드는 바로 사용이 가능하고 어떤 메서드는 앞에 Array나 Object를 붙여줘야하나 궁금했는데 이런 이유였군

 


어떤 객체의 .constructor에 접근해서 변경이 가능하다

-> 생성자 정보를 알아내기 위해 constructor 프로퍼티에 의존하는게 항상 안전한 것은 아님 

 

변경하더라도 이미 만들어진 인스턴스의 원형이 바뀌지는 않는다.

생성자 함수 A의 인스턴스 a에 대해

a.constructor  = B 라고 접근하여 변경하더라도 

a instanceof B 는 false를 반환한다. 


메서드 오버라이드 

동일한 이름의 프로퍼티에 메서드를 할당하면 덮어쓰기가 아닌 얹는 방식의 오버라이드가 일어난다

 

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

Person.prototype.getName = function (){
	return this.name;
}

var iu = new Person('지금');
iu.getName = function (){
	return '바로' + this.name;
}

console.log(iu.getName()) // (1) 바로 지금 

console.log(iu.__proto__.getName()) // undefined;

iu.__proto__.name = '이지금'; 
console.log(iu.__proto__.getName()) // (2) 이지금

console.log(iu.__proto__.getName.call(iu)); // (3) 지금

아이유 좋아하시나보다

 

Person 생성자 함수의 prototype 객체의 getName 프로퍼티에 메서드를 정의하고, 

생성된 인스턴스의 getName 프로퍼티에 에 접근해 다른 메서드로 변경했을때 

 

iu.getName은 오버라이드하여 변경된 메서드가 실행되지만 

iu.__proto__ 로 접근하면 (name 프로퍼티도 설정해주면) 원래 메서드가 실행된다. (이때의 this는  iu.__proto__

iu.__proto__  =  Person.prototype 을 참조하기 때문이지..

 

getName 이라는 메서드가 호출되면 ?
자바스크립트 엔진은 가장 가까운 대상인 자신의 프로퍼티를 검색하고
없으면 그 다음으로 가까운 대상인 __proto__ 를 검색하는 순으로 진행된다. 

 

__proto__ 로 getName 호출하더라도 ( 이때의 this는  iu.__proto__ )

call이나 apply 로 this 바인딩을 해주면 iu 가 this에 바인딩이 된다. 

 

iu.__proto__.getName.call(iu)

 

즉 메서드가 오버라이드 되더라도, 원래의 메서드에 __ proto__ 로 우회하여 접근할 수 있다. 


프로토타입 체인

어떤 데이터의 __proto__ 프로퍼티 내부에 다시 __proto__ 가 연쇄적으로 이어져 프로토타입 체인을 형성

이 체인을 따라 검색하는 것을 프로토타입 체이닝 이라고 한다. 

 

프로토타입 체인

어떤 생성자 함수이든 prototype = 객체 이므로 

Object.prototype 이 프로토타입 체인 최상단에 존재한다. 

 

따라서 Object.prototype 에 있는 메서드를 변경하면, 하위의(?) 다른 객체가 호출했을 때 영향을 미친다. 

 

또한 어떤 데이터에서든 활용할 수 있는 범용적인 메서드만 있다. 

즉 객체에서만 사용할 메서드는 다른 여느 데이터 타입처럼 프로토타입 객체 안에 정의할 수 없다. 

 

두 단계 이상의 체인을 지니는 다중 프로토타입 체인도 가능하다

(이 내용은 생략... ) 


다음 찐막 챕터는 '클래스'이다. 

책 반납일 전에 반드시 공부할 것 ^^..

이 포스팅은 <코어 자바스크립트> 를 읽고 내용을 정리한 포스팅입니다. 틀린 내용이 있다면 알려주세요 _ _