Corejavascript_06.prototype(2)

손병진·2020년 12월 14일
0

CoreJavaScript

목록 보기
9/10

해당 내용은 '코어 자바스크립트' 서적을 참고하여 작성되었으며, 초보 개발자에게 유익한 책을 지필해주신 지은이 정재남 님께 감사의 말씀을 드립니다.

프로토 타입 체인

메서드 오버라이드

  • 앞선 블로그 에서 확인했던 것과 같이 인스턴스는 프로토타입을 통해 자신의 생성자의 함수의 메서드를 가져올 수 있다고 했다.
    그렇다면 만약 인스턴스에서 별도로 만든 메서드가 있을시 그 이름이 생성자 함수의 메서드 이름과 같다면 어떠할까?
// 생성자함수 정의
var Person = function (name) {
  this.name = name;
};

// 메서드 정의
Person.prototype.getName = function () {
  return this.name;
};

// 인스턴스 정의
var instance = new Person('son');

// 인스턴스의 메서드 정의(프로토타입 메서드와 이름이 같다)
instance.getName = function () {
  return '새로운' + this.name;
};

console.log(instance.getName()); // 새로운 son (출력값)
//위 예시에서 보면 기존 생성자 함수의 메서드가 아닌 인스턴스의 메서드가 실행되었음을 알 수 있다.
  • 그렇다면 기존 생성자 함수의 메서드는 없어졌을까? 아니다

  • 이런 현상을 메서드 오버라이드 라고 한다.
    인스턴스 자체의 메서드 식별자와 생성자 함수의 메서드 식별자 같을 때, 인스턴스에서는 자신이 가진 메서드를 실행시켜 기존에 받아왔던 메서드를 덮어버리는 현상이다.

  • 그렇다면 우회적인 접근이 가능할까? 그렇다

console.log(instance.__proto__.getName.call(instance)); // son
// 이렇게 __proto__ 프로퍼티를 활용하고, call 메서드로 this를 지정해주어 실행시킬 수 있다.

프로토타입 체인

  • 이 개념을 살펴보기 위해 대표적인 생성자인 Array 객체 구조를 참고할 수 있다.
console.dir(Array);
/* 굉장히 길게 출력되는데 여기서 주요하게 확인하는 부분은 __proto__ 이다.
ƒ Array()
  arguments: (...)
  caller: (...)
  from: ƒ from()
  isArray: ƒ isArray()
  length: 1
  name: "Array"
  of: ƒ of()
  prototype: [constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
  Symbol(Symbol.species): (...)
  get Symbol(Symbol.species): ƒ [Symbol.species]()
  
  __proto__: ƒ () // 앞서 배웠다시피 해당 프로퍼티가 존재한다. 그런데 그 안을 보면
    apply: ƒ apply()
    arguments: (...)
    bind: ƒ bind()
    call: ƒ call()
    caller: (...)
    constructor: ƒ Function()
    length: 0
    name: ""
    toString: ƒ toString()
    Symbol(Symbol.hasInstance): ƒ [Symbol.hasInstance]()
    get arguments: ƒ ()
    set arguments: ƒ ()
    get caller: ƒ ()
    set caller: ƒ ()
    
    __proto__: Object // 여기 똑같은 프로퍼티 하나 더 있는 것을 알 수 있다.
    [[FunctionLocation]]: <unknown>
    [[Scopes]]: Scopes[0]
  [[Scopes]]: Scopes[0]
*/
  • 왜냐하면 prototype 또한 하나의 객체이기 때문에 Object 생성자의 prototype을 받는 것이다.

  • 이런식으로 __proto__ 속성이 연쇄적으로 이어진 것을 프로토타입 체인 이라고 하며,
    이 체인을 따라가는 것을 프로토타입 체이닝 이라고 한다.

  • 그렇다면 모든 생성자 함수에는 prototype 프로퍼티가 존재하는데, 그럼 모두 Object prototype을 상속받는가? 그러하다


객체 전용 메서드의 예외사항

  • 어떤 생성자 함수이든 prototype은 반드시 객체이기 때문에 Object.prototype이 언제나 프로토타입 체인의 최상단에 존재하고 있다.
  • 그렇기 때문에 Object 생성자함수 메서드에는 두가지 특징이 있다.
    1. 객체 전용 메서드는 prototype 프로퍼티가 아닌 static 메서드로 부여되어 있다.
    2. Object.prototype 내의 메서드는 범용적으로 활용할 수 있는 것들로만 구성되어 있다.
// 객체 인스턴스 정의
var instance = new Object();

// 객체 메서드 정의
Object.prototype.getArr = function () {
  var result = [];
  for (prop in this) {
    if (this.hasOwnProperty(prop)) {
      result.push(prop);
    }
  }
  return result;
};

// 데이터 정의
var data = [{ a: 1, b: 2, c: 3 }, [1, 2, 3], 'abc', true];

// 각 요소별로 객체 메서드 실행
data.forEach((el) => {
  console.log(el.getArr());
});
/*
[ 'a', 'b', 'c' ]
[ '0', '1', '2' ]
[ '0', '1', '2' ]
[]
모두 에러없이 실행된다.
여기 주목해야할 점은 객체가 아닌 데이터 타입인데도 불구하고 객체 메서드가 실행된다는 점이다.
*/
  • 그렇기 때문에 Object 생성자함수는 예외적으로 객체 전용 메서드를 prototype 안이 아닌 바깥에 static 메서드 (ex. Object.freeze()) 로써 두며,
    범용적으로 사용할 메서드는 Object.protype 내에 두는 것이다.
    왜냐하면 Object.prototype 이 프로토타입 체인의 최상단에 있긴 때문이다.

다중 프로토타입 체인

  • 자바스크립트 기본 내장 데이터타입의 경우 프토토타입 체인이 1,2 단계에서 끝나지만, 직접 생성자 함수를 만들경우, 그 이상도 가능하다.
// 생성자 함수 정의
var Constructor = function () {
  let num = 0;
  for (el in arguments) {
    this[el] = arguments[el];
    num++;
  }
  this.length = num;
};

var instance = new Constructor(1, 2, 3);

console.dir(instance);
/* 출력값을 보면 데이터 형태는 배열과 유사하다.
Constructor
  0: 1
  1: 2
  2: 3
  length: 3
  __proto__:
    constructor: ƒ ()
    __proto__: Object // 하지만 배열 메서드를 사용할 수 없다(유사배열객체).
*/
  • 여기서 배열 메서드가 가능하게 하려면,
    Constructor.prototype = [] 명령어만 추가하면 된다.
// 기존
var instance = new Constructor(1, 2, 3);
instance.pop() // instance.pop is not a function

// 변경(추가)
Constructor.prototype = [];
var instance = new Constructor(1, 2, 3);
console.log(instance.pop()); // 3 출력
  • 이를 통해 Constructor 생성자함수 상위에 Array 그 상위에 Object 까지(3단계) 연결된 것이다.
    이렇게 다중 프로토타입 체인을 만들 수도 있다.
profile
https://castie.tistory.com

0개의 댓글