Frontend/JS.info 정리

Prototype - 프로토타입 상속

Creative_Lee 2022. 6. 17. 13:35

기존의 있는 기능을 가져와 확장하고 싶을 때,

자바스크립트 고유 기능인 프로토타입 상속을 사용할 수 있습니다!


1. [[Prototype]]

자바스크립트의 객체는 [[Prototype]] 이라는 숨김 프로퍼티를 갖습니다.

이 숨김 프로퍼티 값은 null 혹은 다른 객체에 대한 참조입니다.

이때 다른 객체를 참조하는 경우, 참조 대상을 프로토타입이라고 부릅니다.

객체의 숨김프로퍼티 [[Prototype]]이 가지는 값이 다른 객체에 대한 참조값 이라면 프로토타입 객체라고 부른다.

객체에서 프로퍼티를 읽으려고 할 때, 프로퍼티가 없다면,

자바스크립트는 자동으로 프로토타입 객체에서 프로퍼티를 찾습니다.

이런 동작 방식을 '프로토타입 상속' 이라고 부릅니다.

 


[[Prototype]] 프로퍼티는 내부 숨김 프로퍼티이지만 개발자가 값을 설정할 수 있습니다.

__proto__를 사용하면 객체의 프로토타입을 설정할 수 있습니다.

let animal = {
  walk : true
}

let human = {
  think : true
}

human.__proto__ = animal // 프로토타입 객체

console.log(human.walk) // true

human 객체에 walk 프로퍼티가 없으므로, 프로토타입 객체인 animal에서 walk를 찾습니다.

'human의 프로토 타입은 animal' 이고,

'human은 animal을 상속받는다.' 라고 말합니다.

프로토타입에서 상속받는 프로퍼티는 '상속 프로퍼티' 라고 부릅니다.

 


프로토 타입 체인은 길어질 수 있으며 3가지 제약사항이 있습니다.

 

1. 순환 참조가 허용되지 않습니다.

2. __proto__의 값으로는 다른 객체, null만 허용됩니다.

3. 객체엔 오직 하나의 [[Prototype]] 만 존재 하고, 객체는 2개의 객체를 상속받지 못합니다.

 


2. 프로토타입은 읽기 전용입니다.

프로토타입은 프로퍼티를 읽을 때만 사용합니다.

프로퍼티의 수정, 삭제는 객체에서 직접 해야합니다.

let animal = {
  walk : true,
  eat() {
    console.log('동물은 잘 먹어요.')
  }    
}

let human = {
  think : true
}

human.__proto__ = animal

human.eat = function(){
  console.log('사람은 잘 먹어요.')
}

human.eat() // 사람은 잘 먹어요.

위 코드 처럼 

human.eat = ...  의 동작은 프로토타입 객체인 animal의 eat을 수정하지 않고,

human 객체에 eat() 메소드를 추가하고 실행합니다.

 


3. this가 나타내는 것 

this는 프로토타입에 영향을 받지 않습니다.

let user = {
  firstName: 'Bob',
  lastName: 'Do',

  set fullName(value){
    [this.lastName, this.firstName] = value.split(" ");
  },

  get fullName(){
    return `${this.lastName} ${this.firstName}`
  }
}

let king = {
  __proto__: user,
  isRoyal: true
}

console.log(king.fullName) // Do Bob

king.fullName = 'Do God'

console.log(king.fullName) // Do God
console.log(user.fullName) // Do Bob

위 코드처럼

king.fullName을 log찍으면  get fullName이 호출됩니다.

이후 king.fullName에 'Do God'을 할당하면 set fullName이 호출됩니다.

이때 this의 값은 .앞에 있는 객체, 즉 king 객체 입니다.

때문에 set fullName 함수로 인하여

this.lastName = Do ,

this.firstName = God 으로 2개의 프로퍼티가 king 객체에 생성됩니다.

 

객체 하나에 메서드를 많이 구현해 놓은 다음, 다른 여러 객체에서 메서드 객체를 상속 받아

사용하는 경우가 많기 때문에, 위와 같은 this의 특징을 알아두어야 합니다.

 

이를 통해 메서드는 공유되지만, 객체의 상태는 공유되지 않는다 라는 결론을 내릴 수 있습니다.


4. for..in 반복문

for..in 반복문은 상속 프로퍼티도 순회대상에 포함시킵니다.

let animal = {
  walk : true, 
}

let human = {
  __proto__ : animal,
  think : true,
}

for(let prop in human){
  console.log(prop) // think, walk
}

console.log(Object.keys(human)) // ['think']

obj.hasOwnProperty(key) 메소드는

key에 대응하는 property가 obj에 직접 구현되어 있는 property일 때만 true를 리턴합니다.

let animal = {
  walk : true, 
}

let human = {
  __proto__ : animal,
  think : true,
}

for(let prop in human) {
  let isOwnProp = human.hasOwnProperty(prop);

  if(isOwnProp) {
    console.log(`객체 자신의 프로퍼티: ${prop}`); // 객체 자신의 프로퍼티: think
  } else {
    console.log(`상속 프로퍼티: ${prop}`); // 상속 프로퍼티: walk
  }
}

위 예시에서

human은 animal을,

animal은 Object.prototype을,

Object.prototype은 null을 상속받고 있습니다.

animal이 Object.prototype을 상속받는 이유는 animal을 객체 리터럴 방식으로 선언했기 때문입니다.

 

hasOwnProperty 메소드는 Object.prototype에 선언되어 있으므로 상속 프로퍼티 이지만,

hasOwnProperty 프로퍼티가 enumerable 플래그가 false 이므로 열거가 불가능 합니다.

때문에 for..in 문으로 열거한 상속 프로퍼티에 출력되지 않았습니다.

 

 

요약

1. 자바스크립트의 모든 객체에는 [[Prototype]] 이라는 내부 숨김 프로퍼티가 있고 또 다른 객체나 null을 가리킨다.

2. obj. __proto__로 프로토타입에 접근 가능하다.

3. 객체의 프로퍼티를 읽으려고 할 때, 프로퍼티가 없다면, 프로토타입에서 프로퍼티를 찾아간다.

4. 일반적인 데이터 프로퍼티를 다룰때, 수정, 삭제와 관련된 연산은 객체에 직접 적용됩니다. ( 프로토 타입은 읽기 전용)

5. 프로토타입으로 상속받은 메소드라도 메소드의 this는 호출대상인 .앞의 객체를 가리킨다.

6. for..in 문은 상속프로퍼티도 순회한다.

 

 

 

기본이 중요하다.