◾️ 모든 내장 생성자 함수에서 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 네이티브 프로토타입]
'모던 자바스크립트' 카테고리의 다른 글
8.4 프로토타입 메서드와 __proto__가 없는 객체 (0) | 2020.09.23 |
---|---|
8.2 함수의 prototype 프로퍼티 (0) | 2020.09.10 |
8.1 프로토타입 상속 (0) | 2020.09.09 |
6.10 화살표 함수 다시 살펴보기 (0) | 2020.09.02 |
6.10 함수 바인딩 (0) | 2020.09.01 |