Prototype

Happhee·2022년 5월 2일
0

AvenJS

목록 보기
8/17

✨ Prototype

한마디로 유전자이다.

부모가 자식에게 물려주기 위해서 사용하는 것으로 이를 프로그래밍적으로 접근하여 해석하면, Prototype 객체는 생성자 함수에 의해 생성된 각각의 객체에 대한 공유 프로퍼티를 제공하고자 사용하는 것으로 볼 수 있다.

자바스크립트에서는 [[Prototype]]이라는 슬롯을 가지고, 이것이 상속을 구현하는 데 사용된다.
여기서 [[Prototype]]__proto__로 접근가능하며 이는 Object.getPrototypeOf를 호출하게 된다.

👇 예제 코드로 이해해보자.

function fruit(){
  this.name = "apple";
  this.quantity = 12;
}
let myFruit = new fruit();
console.log(myFruit);

myFruit에게 fruit가 전달이되면서 그 안에는 [[Prototype]]이라는 슬롯이 생성되어 있음을 알 수 있다.

즉, fruit가 prototype을 가지고 있다는 것이다.

fruit.prototype의 결과를 보면 확인할 수 있다.


__proto__

자식에서 부모의 prototype에 접근하기 위해 사용되는 [[Prototype]] getter / setter이다.

앞선 코드에서의 부모는 fruit이고 자식은 myFruit이다.
그래서 fruit이 prototype에 접근하고 싶을 때에는 .prototype을 사용하고, myFruit이 prototype에 접근하고 싶을 때에는 __proto__를 사용해야 한다.

이러한 원리로 우리가 원하는 부모 / 자식의 상속관계를 __proto__로 구현해볼 수 있다.

Prototype chain

const tree = {
  fruits : true,
  tall : "100m",
  myFruit(){
    console.log(`나무의 높이는 ${this.tall}입니다`);
  }
};
const orange = {
  quantity : 12,
  __proto__ : tree,
}

console.log(orange.__proto__);

tree의 자식으로 orange가 설정되었다.

이제부터 orange는 tree의 속성에 대해 참조할 수 있는 권한이 생겼다.

orange에 존재하는 속성 quantity 뿐만 아니라 tree에 존재하는 fruits, tail에도 접근이 가능하다. 이것이 흔히 말하는 prototype chain이다.

하지만, 부모인 tree에서 orange의 quantity에 접근하는 것은 불가능하다.
왜냐히면 prototype chain은 상위로 올라가는 것만 가능하기 때문이다.

❗️ 주의 사항 ❗️

프로토타입 체이닝을 구현할 때, 주의해야 하는 2가지가 있다.
1. 순환 참조는 허용되지 않는다.

  1. __proto__의 값은 객체null만 가능하다.

✨ 특징

읽기 전용

프로토타입은 프로퍼티를 읽을 떄만 사용하기에 프로퍼티를 추가 / 수정 / 삭제 등에 대한 연산은 객체에 직접 실행해야 한다.

👇 예제 코드를 살펴보자.

const tree = {
  fruits : true,
  tall : "100m",
  myFruit(){
    console.log(`나무의 높이는 ${this.tall}입니다`);
  }
};
const orange = {
  quantity : 12,
  __proto__ : tree,
}

현재 orange.myFruit()가 참조되는 곳은 tree의 메서드 이다.
객체 orange에 메서드를 추가하기 전에 tree에서 메소드를 바꿔보자.

tree.myFruit = function(){ 
  console.log("나는 나무입니다")
}

변경된 함수를 실행하는 것을 알 수 있다.

여기서 myFruit()에 다른 함수를 할당시켜보자.

orange.myFruit = function(){
  console.log("나는 오렌지입니다");
}

orange.myFruit()이 tree에서 참조되지 않고 객체 orange에 직접 추가한 메서드가 실행됨을 알 수 있다.

set, get

const tree = {
  fruit : "orange",
  tall : "100m",
  set myFruit(value){
    [ this.fruit, this.tall ] = value.split(" ");
  },
  
  get myFruit(){
    return `${this.fruit} ${this.tall}`;
  }
};
const orange = {
  quantity : 12,
  __proto__ : tree,
}

orange.myFruit을 출력하면 getter함수가 실행되고, 여기서 orange.myFruit을 setter로 바꿔보자.

orange에서 호출되는 getter의 값과 tree에서 호출되는 getter의 결과 값이 달라지는 것을 알 수 있다.

this

this는 프로토타입에 영향을 받지 않고, 메서드를 객체 / 프로토타입에서 호출하든 .앞에 있는 객체를 가리킨다.

👇 예제 코드를 보자.

const tree = {
  myFruit(){
    console.log(`과일 이름은 ${this.fruit}이고, 나무의 높이는 ${this.tall}입니다`);
  },
  initFruit(){
    this.fruit = "orange";
    this.tall = "100m";
  }
};
const orange = {
  quantity : 12,
  __proto__ : tree,
}

tree, orange 모두 fruit, tall에 대한 프로퍼티를 가지고 있지 않기에 아래와 같은 결과를 나타낸다.

여기서 orange에 프로퍼티를 추가해보자.

orange.initFruit();

tree에는 여전히 undefined의 값으로 전달되지만, orange에는 fruit, tall에 대한 프로퍼티가 추가된 것을 확인할 수 있다.

for ... in

이에 대한 확인을 for ... in 반복문을 통해 진행하면 다음과 같다.

tree의 프로퍼티로는 myFruit, initFruit만이 존재한다.

for(const property in tree ) console.log(property);

반면, orange의 프로퍼티로는 quantity, fruit, tall, myFruit, initFruit가 존재하는 것을 알 수 있다.

for(const property in orange) console.log(property);

hasOwnProperty(key)

상속 프로퍼티를 제외하고 순회하고 싶을 때 사용하는 메서드이다.

for(const property in orange) {
  const isOwn = orange.hasOwnProperty(property);
  if(isOwn) console.log(property, "🍊오렌지랍니다🍊");
  else console.log(property, "내 것이 아니야!!!");
}

orange만의 프로퍼티인 quantity, fruit, tall만이 hasOwnProperty의 true 값을 가지게 된다.


📚 학습할 때, 참고한 자료 📚

profile
즐기면서 정확하게 나아가는 웹프론트엔드 개발자 https://happhee-dev.tistory.com/ 로 이전하였습니다

0개의 댓글