TIL 57 day 6장 프로토타입

Winney·2021년 1월 7일
0

코어자바스크립트

목록 보기
6/6
post-thumbnail

프로토타입

  • JavaScript는 프로토타입(prototype)기반 언어
    : 어떤 객체를 원형(prototype)을 삼고 이를 복제(참조)해 상속과 비슷한 효과

개념 (constructor, prototype, instance)

var instance = new Constructor();

  • prototype은 객체 : prototype 객체 내부에 인스턴스가 사용할 메서드를 저장
    => 인스턴스도 숨겨진 프로퍼티인 __proto__를 통해 이 메서드들에 접근 가능
    instance.__proto__ 방식으로 직접 접근 불가 => 브라우저 호환성을 위해 지원하고는 있으나 권장되지 않음
    Object.getPrototypeOf(instance)/Refelect.getPrototypeOf(instance)를 통해서 접근
var Person = function(name){
  this.name = name;
}
Person.prototype.getName = function() {
  return this._name;
}

var suzi = new Person('suzi');
suzi.__proto__.getName(); // undefined (error 나지 않음)
Person.prototype === suzi.__proto__ // true
  • 에러나지 않는다. => getName이 실행되었다. getName은 함수다!
    => this에 바인딩된 대상이 잘 못되어서 예상한 값이 도출 되지 않은 것
  • getName 함수 내부에서의 this는 suzi가 아니라 suzi.__proto__라는 객체
    => __proto__는 생략 가능한 프로퍼티이다.
suzi.__proto__.getName
=> suzi(.__proto__).getName
=> suzi.getName

생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 마치 자신의 것처럼 해당 메서드나 프로퍼티에 접근할 수 있게 된다.

var Constructor = function (name) {
  this.name = name;
}
Constructor.prototype.method1 = function(){};
Constructor.prototype.property1 = 'Constructor Prototype Property';

var instance = new Constructor('Instance');
console.dir(Constructor); // 1번
console.dir(instance); // 2번


2번이 Contructor로 출력되는 이유는 인스턴스가 해당 생성자 함수의 이름을 표기해 해당 함수의 인스턴스임을 표기하기 때문이다.

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

  • 인스턴스의 __proto__은 Array.prototype을 참조
    => __proto__가 생략 가능하도록 설계
    => 인스턴스가 push, pop, forEach 등의 메서드를 마치 자신의 것처럼 호출 가능
  • Array의 prototype 프로퍼티 내부에 있지 않은 from, isArray 등의 메서드는 인스턴스가 직접 호출 불가능
    => Array 생성자 함수에서 직접 접근해야함

constructor 프로퍼티

  • 생성자 함수의 프로퍼티인 prototype 객체 내부에는 constructor라는 프로퍼티가 존재
    => 원래 생성자 함수(자기자신)을 참조
    => 인스턴스로부터 그 원형이 무엇인지 알 수 있는 수단이다.
var arr = [1,2];
Array.prototype.constructor === Array // true
arr.__proto__.constructor === Array // true
arr.constructor === Array //true

var arr2 = new arr.constructor(3, 4);
console.log(arr2); // [3, 4]
  • constructor 변경을 하더라도 참조 대상만 바뀐다.
    => 어떤 인스턴스의 생성자 정보를 알아내기 위해 constructor 프로퍼티에 의존하는 게 항상 안전하지 않다.
  1. 다음은 모두 동일한 대상을 기리킨다.
[Constructor]
[instance].__proto__.constructor
[instance].constructor
Object.getPrototypeOf([instance]).constructor
[Constructor].prototype.constructor
  1. 다음은 모두 동일한 객체(prototype)에 접근 가능하다.
[Constructor].prototype
[instance].__proto__
[instance]
Object.getPrototypeOf([instance])

프로토타입 체인

1) 메서드 오버라이드

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()); // 바로 지금
  • iu.__proto__.getName이 아닌 iu 객체에 있는 getName 메서드가 호출
    => 메서드 오버라이드
  • 메서드 오버라이딩 상황에서 prototype에 접근 할 경우
    : console.log(iu.__proto__.getName()); // undefined
    => this가 prototype 객체(iu.__prototype)를 가리키는데 prototype 상에는 name 프로퍼티가 없기 때문, prototype에 name 프로퍼티가 있었으면 그 값을 출력 했을 것.

    일반적으로 메서드 오버라이드가 된 경우 자신으로부터 가장 가까운 메서드에만 접근되나 그 다음으로 가까운 __proto__에 접근도 가능하다.

2) 프로토타입 체인

  • 모든 객체의 __proto__에는 Object.prototype이 연결
var arr = [1, 2];
arr(.__proto__).push(3);
arr(.__proto__)(.__proto__).hasOwnProperty(2); //true
  • 프로토타입 체인(prototype chain) : 어떤 데이터의 __proto__프로퍼티 내부에 다시 __proto__프로퍼티가 연쇄적으로 이어진 것
  • 프로토타입 체이닝(prototype chaining) : 체인을 따라가며 검색하는 것
var arr = [1, 2];
Array.prototype.toString.call(arr); // 1, 2
Object.prototype.toString.call(arr); // [object Array]
arr.toString(); // 1, 2

arr.toString = function() {
  return this.join('_');
};
arr.toString(); //1_2

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

  • 어떤 생성자 함수이든 prototype은 반드시 객체이기 때문에 Object.prototype이 언제나 프로토타입 체인의 최상단에 존재한다. 참조형 데이터뿐만 아니라 기본형 데이터도 __proto__에 반복 접근 가능함으로서 최종적으로 Object.prototype에 접근 할 수 있기 때문
    => 객체만을 대상으로 동작하는 전용 메서드들은 Object.prototype이 아닌 Object에 스태틱(static method)로 부여할 수밖에 없었다!

4) 다중 프로토타입 체인

var Grade = function() {
  var args = Array.prototype.slice.call(arguments);
  for (var i = 0; i <args.length; i++) {
    this[i] = args[i]
  }
  this.length = args.length;
};
var g = new Grade(100, 80);

Grade.prototype = [];

console.log(g);
g.pop();
console.log(g);
g.push(90);
console.log(g);
  • 배열의 형태를 지니지만, 배열의 메서드를 사용할 수는 없는 유사배열객체
  • 인스턴스에서 배열 메서드를 직접 쓸 수 있게끔하고 싶다!
    => g.__proto__, 즉 Grade.prototype이 배열의 인스턴스를 바라보게 하면 된다.
profile
프론트엔드 엔지니어

0개의 댓글