본문 바로가기

모던 자바스크립트

8.3 네이티브 프로토타입

◾️ 모든 내장 생성자 함수에서 prototype을 프로퍼티로 사용한다.

Object.prototype

let obj = {};
alert( obj ); // "[object Object]"

◾️ obj는 비어 있는데도 [object Object] 문자열을 출력한다. 

◾️ obj = new Object()를 줄이면 obj = {}가 된다.

◾️ Object는 내장 객체 생성자 함수인데, 이 생성자 함수의 prototype은 toString을 비롯한 다양한 메서드가 구현되어있는 거대한 객체를 참조한다. 

 

 

 

 

◾️ new Object()를 호출하거나 리터럴 문법 { ... }을 사용해 객체를 만들 때, 새롭게 정의된 객체의 [[Prototype]]은 Object.prototype을 참조한다. 

 

 

 

 

 

 

 

 

◾️ obj.toString()을 호출하면 Object.prototype에서 해당 메서드를 가져오게 된다. 

let obj = {};

alert( obj.__proto__ === Object.prototype ); // true
// obj.toString === obj.__proto__.toString == Object.prototype.toString

⚠️ Object.prototype 위의 체인에는 [[Prototype]]이 없다. 

alert( Object.prototype.__proto__); // null

 

 

다른 내장 프로토타입

◾️ Array, Date, Function을 비롯한 내장 객체들 역시 프로토타입에 메서드를 저장해 놓는다. 

◾️ 배열 [1, 2, 3]을 만들면 기본 new Array() 생성자가 내부에서 사용되기 때문에 Array.prototype이 배열 [1, 2, 3]의 프로토타입이 된다. 

◾️ Array.prototype은 배열 메서드도 제공한다. 

◾️ 이런 내부 동작은 메모리 효율을 높여준다. 

◾️ 명세서에선 모든 내장 프로토타입의 꼭대기엔 Object.prototype이 있어야 한다고 규정한다. → 모든 것은 객체를 상속 받는다. 

let arr = [1, 2, 3];

// arr는 Array.prototype을 상속받았다. 
alert( arr.__proto__ === Array.prototype ); // true

// arr는 Object.prototype을 상속받았다. 
alert( arr.__proto__.__proto__ === Object.prototype ); // true

alert( arr.__proto__.__proto__.__proto__ ); // null

◾️ 체인 상의 프로토타입엔 중복 메서드가 있을 수 있다. Array.prototype엔 요소 사이에 쉼표를 넣어 요소 전체를 합친 문자열을 반환하는 자체 메서드 toString이 있다. 

let arr = [1, 2, 3];

alert(arr); // 1, 2, 3 <-- Array.prototype.toString의 결과

◾️ Object.prototype에도 메서드 toString이 있다. 

◾️ 중복 메서드가 있을 때는 체인 상에서 가까운 곳에 있는 메서드가 사용된다. 

◾️ Array.prototype이 체인상에서 더 가깝기 때문에 Array.prototype의 toString이 사용된다. 

◾️ console.dir를 사용하면 내장 객체의 상속 관계를 확인하는데 도움이 된다. 

 

◾️ 함수는 내장 객체 Function의 생성자를 사용해 만들어지는데 call, apply를 비롯한 함수에서 사용할 수 있는 메서드는 Function.prototype에서 가져 온다. 

function f() {}

alert(f.__proto__ == Function.prototype ); // true
alert(f.__proto__.__proto__ == Object.prototype ); // true; 객체에서 상속 받음

 

 

원시값

◾️ 문자열과 숫자, 불린값은 객체가 아니다. 그런데 이런 원시값들의 프로퍼티에 접근하려고 하면 내장 생성자 String, Number, Boolean을 사용하는 임시 래퍼(wrapper)객체가 생성된다. 임시 래퍼 객체는 이런 메서드를 제공하고 난 후에 사라진다. 

◾️ 래퍼객체는 보이지 않는 곳에서 만들어 진다. 최적화는 엔진이 담당한다. 

◾️ 명세서에서는 각 자료형에 해당하는 래퍼 객체의 메서드를 프로토타입 안에 구현해 놓고 String.prototype, Number.prototype, Boolean.prototype을 사용해 쓸 수 있도록 규정한다. 

⚠️ null과 undefined에 대응하는 래퍼 객체는 없다. 

따라서 undefined에선 메서드와 프로퍼티를 이용할 수 없다. 물론 프로토타입도 사용할 수 없다. 

 

 

네이티브 프로토타입 변경하기[#native -prototype-change]

 

◾️ 네이티브 프로토타입을 수정할 수 있다 .

String.prototype.show = function() {
  alert(this);
};

"Hello".show(); // Hello
⚠️ 프로토타입은 전역으로 영향을 미치기 때문에 프로토타입을 조작하면 충돌이 날 가능성이 높다.
두 라이브러리에서 동시에 String.prototype.show 메서드를 추가하면 한 라이브러리의 메서드가 다른 라이브러리의 메서드를 덮어쓴다.

→ 그렇기 때문에 네이티브 프로토타입을 변경하는 것은 좋은 방법이 아니다. 

◾️ 모던 프로그래밍에서 네이티브 프로토타입 변경을 허용하는 경우는 폴리필을 만들 때 뿐이다 

◾️ 폴리필은 자바스크립트 명세서에 있는 메서드와 동일한 기능을 하는 메서드 구현체를 의미한다. 

◾️ 명세서에는 정의되어 있으나 특정 자바스크립트 엔진에서는 해당 기능이 구현되어있지 않을 때 폴리필을 사용한다. 

◾️ 폴리필을 직접 구현하고 난 후 폴리필을 내장 프로토타입에 추가할 때만 네이티브 프로토타입을 변경하는 것이 좋다. 

if(!String.prototype.repeat){ // repeat이라는 메서드가 없다고 가정
  // 프로토타입에 repeat를 추가
  
  String.prototype.repeat = function(n) {
  	// string을 n회 반복(repeat)
    return new Array(n + 1).join(this);
  };
}

alert( "Hi".repeat(3) ); // HiHiHi

 

 

프로토타입에서 빌려오기

◾️ 한 객체의 메서드를 다른 객체로 복사할 때 사용된다. 

let obj = {
  0: "Hello",
  1: "world",
  length: 2,
};

obj.join = Array.prototype.join;

alert( obj.join(',') ); // Hello,world!

◾️ 내장 메서드 join의 내부 알고리즘은 제대로 된 인덱스가 있는지와 length 프로퍼티가 있는지만 확인한다. 

◾️ obj.__proto__를 Array.prototype로 설정해 배열 메서드를 상속받는 방법이 있다. 이렇게 하면 obj에서 모든 Array메서드를 사용할 수 있다. 

◾️ 그런데 이 방법은 obj가 다른 객체를 상속 받고 있을 때는 사용할 수 없다. 자바스크립트는 단일 상속만을 허용한다. 

 

 

📝 요약

◽️ 모든 내장 객체는 같은 패턴을 따른다. 
    ◽️ 메서드는 프로토타입에 저장된다(Array.prototype, Object.prototype, Date.prototype 등)
    ◽️ 객체 자체엔 데이터만 저장한다.(배열의 요소, 객체의 프로퍼티, 날짜 등).
◽️ 원시값 또한 래퍼 객체의 프로토타입에 Number.prototype, String.prototype, Boolean.prototype 같은 메서드를 저장한다.  undefined와 null값은 래퍼 객체를 가지지 않는다. 
◽️ 내장 프로토타입을 수정할 수 있다. 내장 프로토타입의 메서드를 빌려와 새로운 메서드를 만드는 것 역시 가능하다. 그러나 내장 프로토타입 변경은 되도록 하지 않아야 한다. 내장 프로토타입은 새로 명세서에 등록된 기능을 사용하고 싶은데 자바스크립트 엔진엔 이 기능이 구현되어 있지 않을 때만 변경하는게 좋다. 

 

 


 

 

📌 [JAVASCRIPT_INFO 네이티브 프로토타입]

 

네이티브 프로토타입

 

ko.javascript.info