JS 프로토타입

gang_shik·2025년 3월 19일
0

JavaScript

목록 보기
16/23

프로토타입이란?

  • JS는 내부적으로 프로토타입 기반으로 돌아간다고 해도 무방함(즉, 프로토타입 기반 언어임)
  • __proto__로 접근시, 프로토타입과 관련하여 많은 것을 볼 수 있음(각 타입에서 접근해서 확인할 수 있음)
  • 어떤 값을 만들어도 내부적으로 프로토타입을 가지고 있고, 그래서 프로토타입 기반 언어라고 볼 수 있는것

constructor

  • 생성자의 경우, 앞서 본 생성자 함수, 클래스 내의 생성자 함수 등 다양하게 있음
  • 생성자도 모든 JS 내부에 확인이 됨(마치 프로토타입처럼)
  • 아래의 예시에서 서로 다른 객체여도 동일하게 생성자 함수를 가르키는 것을 알 수 있음
function Person(name, age) {
  this.name = name;
  this.age = age;
}

const kevin = new Person('kevin', 99);
const hart = new Person('hart', 11);

kevin.constructor.name; // Person
hart.constructor.name; // Person
  • 아래와 같이 각각의 타입에 있어서도 만들어진 타입에 맞는 생성자의 이름을 가지고 있음
const obj = {};
const arr = [];
const func = function () {};
const str = 'str'

obj.constructor.name // Object
arr.constructor.name // Array
func.constructor.name // Function
str.constructor.name // String

obj instanceof Object; // true
arr instanceOf Array; // true
func instanceOf Function; // true
  • 그래서 JS가 프로토타입 기반언어라고 볼 수 있음, 내가 만든 값들이 내부적으로 프로토타입을 다 가지고 있고, 이 프로토타입을 통해서 어떤 생성자를 통해서 만들었는지 알 수도 있음
  • instanceof 함수 역시 이러한 생성자를 통해서 확인하는 것(constructor로 확인한 것, 위와 같이), 그래서 instanceof 사용을 잘하면 인스턴스 타입을 확인하는데 쉬움(Primitive의 경우 래퍼 객체를 사용할 때)
  • 이러한 프로토타입에 직접 접근하거나 생성자를 활용할 때, 어디서 파생됐는지 확인하는 경우 instanceof나 생성자 함수를 다 가지고 있기 때문에 . 통해 접근해서 확인할 수도 있음

proto

  • 던더 프로토라고 부름, 이는 브라우저에서 비표준으로 제공하는 것이었음
  • 이를 통해서 프로토타입에 대신 접근하도록 해주는 값임, 마치 getter, setter처럼 프로토타입에 접근하기 용이한 값(일종의 프로토타입의 접근제어자)
  • 하지만 사용을 권장하지 않음, 대신 getPrototypeOf 혹은 setPrototypeOf 사용을 권장함
  • 해당 메소드들은 Object에서부터 시작되는 메소드들임, 이를 통해서 프로토타입의 접근해서 처리하는것을 권장함(사용하게 되는 경우)

프로토타입 체인

// 스코프에 따른 체이닝
{
  const a = 'outer'; // inner가 없으면 체이닝 되어 outer로 console이 나옴

  {
    const a = 'inner'; // inner로 나옴 

    console.log(a);
  }
}

// 메소드 체이닝
[1, 2, 3]
  .sort((a, b) => a - b)
  .filter((e) => typeof e === 'number')
  .map((e) => e + 'EA');
  • 위와 같이 체이닝을 하는데 프로토타입 역시도 비슷하게 체이닝을 할 수 있음, 이는 array가 Array 타입이면서 객체가 될 수 있는 구조이기에 hasOwnProperty 같은 객체의 메소드도 사용 가능함(array < Array < Object)
  • 즉, 그러한 체이닝 구조를 가지고 있다고 볼 수 있음
const array = [ ]

array.push(10)
array.push(20)

// 배열 던더 프로토 활용 가능
array.hasOwnProperty(0) // true
array instanceOf Object // true
  • 아래와 같이도 활용이 될 수도 있는 것임, 아무것도 내부의 프로토타입을 정의하지 않아도, 다양한 메소드를 사용할 수 있는 것이 체이닝이 되어 있기 떄문이라고 볼 수 있음
const animal = {
  sayName() {
    return 'ANIMAL';
  },
};

// dog도 객체로 체이닝이 되어서 sayName 메소드 사용이 가능해짐
const dog = Object.create(animal);
dog.sayName() // ANIMAL

프로토타입 확장

  • 확장은 extends 키워드를 쓰고, 상속이라고도 많이 표현함(부모 => 자식 관계를 많이 따짐, 부모 => 확장 => 자식 느낌으로)
  • 프로토타입을 확장한다고 해도 프로토타입을 직접 확장하진 않고 아래와 같이 확장을 함
// Super Class
function Animal(name, sound) {
  this.name = name;
  this.sound = sound;
}

Animal.prototype.getInfo = function() {
  return (
    this.name +
    '가(이)' +
    this.sound +
    ' 소리를 낸다.'
  );
};

// Sub Class
function Friends(name, sound) {
  // call 메소드를 통해서 Animal의 객체를 명시적으로 바인딩 후 인자 사용
  Animal.call(this, name, sound);
}

// 이때 아래와 같이 프로토타입도 확장을 해줘야 제대로 확장이 됨
Friends.prototype = Object.create(
  Animal.prototype,
);

// 생성자도 마찬가지로 확장
Friends.prototype.constructor = Friends;

const dog = new Friends('개', '멍멍');
const cat = new Friends('고양이', '냐옹');

// 정상적으로 Animal에서의 getInfo를 확장받아서 사용 가능
dog.getInfo();
cat.getInfo();

dog.constructor.name; // Friends 출력됨

// 인스턴스도 확인하면 확장되어서 다 true가 나옴을 알 수 있음
dog instanceof Friends;
dog instanceof Animal;
  • 클래스에서 좀 더 편리하게 사용해서 쓸 수 있음, 프로토타입도 확장이 될 수 있음을 이해하면 됨
profile
측정할 수 없으면 관리할 수 없고, 관리할 수 없으면 개선시킬 수도 없다

0개의 댓글