#3 프로토타입 - 심화 2 - 네이티브 프로토타입

Jake Seo·2021년 10월 10일
0

Object의 내장 프로토타입

[object Object] ?

const obj = {};
alert(obj);

위의 코드를 실행시키면 결과가 어떤 것이 나올지 예측이 가능한가? 정답은 아래와 같다.

응? 나는 alert('[object Object]')를 입력한 적이 없는데?

이전에 배웠던 내용이 잘 기억난다면, 위의 내용은 잘 풀어썼을 때 아래와 같다는 것을 생각해낼 수 있을 것이다.

const obj = new Object();
alert(obj.toString());

{}가 할당된 obj는 자동적으로 Object 객체의 프로토타입인 Object.prototype을 상속 받게 된다. 그말인 즉, 던더(더블 언더) 프로토(__proto__)에 Object.prototype이 들어가게 된다는 이야기이다.

위의 그림과 같은 현상이 일어난다.

alert(Object.prototype.toString());

위와 같은 코드를 작성한 것과 같은 결과이다.

참고로 Object.prototype.__proto__에는 아무것도 없음을 잠깐 생각해두자. 당연히 prototype__proto__로 상속될 것들이 모여있는 건데, 이중으로 상속할 일은 없다.

다른 내장 객체들의 내장 프로토타입과 상속

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

위와 같이 [1, 2, 3]이라는 배열을 만들어보면, __proto__에 들어가는 내용이 Array.prototype에 있는 내용과 일치한다는 것을 알 수 있다.

이렇게 프로토 타입을 상속받음으로써, push(), pop()등 다양한 배열 메소드도 제공받으며, 이런 내부동작 덕에 메모리 효율도 높아진다.

100 이라는 임의의 숫자가 들어있는 상수를 만들고, __proto__에 들어가는 내용을 Number.prototype과 비교하면 또 일치한다.

역시 이번에도 프로토 타입을 통해 toFixed()와 같은 숫자 전용 메소드를 제공받는다.

위와 같은 방식으로 프로토타입은 계층 구조로 연결되어 있다.

위 코드 예제를 보면, 쉽게 이해할 수 있을 것이다.

한번 더 상위로 가면 null이 존재하는 것도 확인할 수 있다.

상속 우선순위

일단 프로토타입이 상속되면, 동일한 이름의 메소드가 있을 때, 나와 가장 가까운 거리에 있는 상속된 메소드를 사용한다.

이를테면 아래의 코드에서는,

const array = [1, 2, 3];
console.log(array.toString());

toString()을 할 때, Object.prototype.toString()도 있지만, 가장 가까이에 있는 Array.prototype.toString()을 사용하게 된다.

원시(Primitive) 타입

이제까지 사용했던 문자열, 숫자, 불린 값은 내장 생성자인 String, Number, Boolean을 사용하는 임시 래퍼(wrapper) 객체에 의해 감싸졌다. 임시 래퍼 객체는 이러한 메서드를 제공한 이후에는 사라진다.

래퍼 객체는 보이지 않는 곳에서 만들어진다. 최적화는 엔진이 담당한다. 명세서에서는 각 자료형에 해당하는 래퍼 객체의 메서드를 프로토타입 안에 구현해놓고, String.prototype, Number.prototype, Boolean.prototype을 사용해 쓸 수 있도록 규정한다.

null과 undefined는?

nullundefined도 자바스크립트에서는 원시 타입으로 분류되는데, 이에 대응하는 래퍼 객체는 존재하지 않는다.

네이티브 프로토타입 변경

String.prototype.log = function() {
  console.log(this.toString()); 
}

"안녕하세요".log();

위와 같은 형식의 조작이 가능하지만, 라이브러리에서 중복으로 네이티브 프로토타입을 바꿔버리면 충돌의 가능성이 매우 크기 때문에 반드시 주의해야 한다.

폴리필

네이티브 프로토타입이 변경되어야 한다면, 보통 그 이유는 오직 폴리필밖에 없다.

특정 브라우저에서 구현되지 않은 메소드를 직접 구현할 때 쓰면 유용하다.

프로토타입 메소드 빌려오기

const obj = {
  0: "Hello",
  1: "World",
  length: 2
};

obj.join = Array.prototype.join;

alert(obj.join(' ')); // Hello World!

내장 메서드인 join의 특성을 이용한 코드이다. 인덱스가 있는지와 length 프로퍼티가 있는지만 확인하기 때문에 이와 같은 동작이 가능하다.

또는, obj.__proto__ = Array.prototype을 통해서도 가능하긴하다. 다만, obj가 이미 다른 객체를 상속받고 있을 때는 불가능하다. 자바스크립트는 단일상속만을 허용하기 때문이다.

코드로 프로토타입 연습해보기

defer 1

Function.prototype.defer = function(ms) {
  setTimeout(this, ms);
};

위 코드를 작성하게 되면, 모든 함수(Function 객체)에 .defer() 메소드를 사용할 수 있게 된다.

defer 2

Function.prototype.defer = function(ms) {
  const f = this;
  return function(...args) {
    setTimeout(() => f.apply(this, args), ms);
  }
}

위와 같이 apply를 이용하면, 넘어온 this를 기억하고, 넘어온 args를 이용하여 데코레이팅 함수도 만들 수 있다.

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글