본문 바로가기

Tech/[Lang] JS & TS

상속과 프로토타입

Hi, There!
안녕하세요, 바오밥입니다.


목차

  1. 개요
  2. 본문
  3. Reference

개요

자바스크립트 객체의 프로토타입에 대해서 알아보도록 하겠습니다.


본문

프로토타입

아래 user 객체는 name 프로퍼티만 정의한 상태입니다.

그런데, 정의하지 않은 hasOwnProperty는 어떻게 사용할 수 있는 걸까요 ?

바로 프로토타입 덕분입니다.

const user = {
  "name": "Mike",
}
console.log(user.name) // "Mike"
console.log(user.hasOwnProperty('name')) // true
console.log(user.hasOwnProperty('age')) // false

 

user 객체 안에 있는 proto (프로토타입) 객체가 이를 지원합니다.

프로토타입 객체는 선언되어 있는 객체에 없는 프로퍼티를 호출했을 때 실행됩니다.

따라서 user 객체 안에 hasOwnProperty 메서드를 정의한다면 프로토타입의 메서드가 아닌 객체에 직접 정의한 메서드가 실행됩니다.

const user = {
  "name": "Mike",
  "hasOwnProperty": function() {
    console.log("test")
  }
}
console.log(user.name) // "Mike"
console.log(user.hasOwnProperty()) // test, undefined
console.log(user.hasOwnProperty()) // test, undefined 가 출력됩니다.

 

이러한 prototype 기능을 이용하여 상속을 구현할 수 있습니다.

상기해 놓은 내용대로 정의된 메서드나 프로퍼티가 없을 경우 프로토타입 객체로 이동하여 메서드와 프로퍼티를 조회합니다.

따라서 bmw와 benz의 공통적인 특성인 바퀴 4개를 car라는 객체로 묶고, 이를 bmw 및 benz 객체의 프로토타입에 집어넣으면 car의 프로퍼티를 bmw와 benz가 상속 받는 꼴이 됩니다.

const car = {
  wheels: 4
}

const bmw = {
  color: "red",
  navigation: 1
}

const benz = {
  color: "black"
}

bmw.__proto__ = car;
benz.__proto__ = car;

 

프로토타입은 여러 번 상속하는 것도 가능합니다.

const car = {
  wheels: 4
}

const bmw = {
  color: "red",
  navigation: 1
}

const benz = {
  color: "black"
}

const x5 = {
  modelVersion: "x5"
}

bmw.__proto__ = car;
benz.__proto__ = car;
x5.__proto__ = bmw;

console.log(x5.wheels) // 4, car를 상속한 bmw를 상속하였기 때문에 4가 출력됨

 

프로토 타입은 숨김 프로퍼티이기 때문에 Object.keys 또는 Object.values 메서드로 조회가 불가능합니다.

const car = {
  wheels: 4
}

const bmw = {
  color: "red",
  navigation: 1
}

const benz = {
  color: "black"
}

const x5 = {
  modelVersion: "x5"
}

bmw.__proto__ = car;
benz.__proto__ = car;
x5.__proto__ = bmw;

console.log(x5, Object.keys(x5), Object.values(x5))

 

 

만약 프로퍼티의 상속 여부를 확인하고 싶은 경우 for문과 hasOwnProperty를 사용해야 합니다.

const car = {
  wheels: 4
}

const bmw = {
  color: "red",
  navigation: 1
}

const benz = {
  color: "black"
}

const x5 = {
  modelVersion: "x5"
}

bmw.__proto__ = car;
benz.__proto__ = car;
x5.__proto__ = bmw;

for (p in x5) {
  if (x5.hasOwnProperty(p)) {
    console.log('객체 프로퍼티 : ', p)
  } else { 
    console.log('상속 프로퍼티 : ', p)
  } 
}

 

 

객체를 정의하지 않고 프로토타입에 바로 프로퍼티를 매핑할 수 있습니다.

덕분에 생성자 함수에도 프로토타입을 매핑해 놓을 수 있습니다.

이 특성을 이용하여 중복된 작업을 효율적으로 줄여줍니다.

const Bmw = function(color) {
  this.color = color;
}

Bmw.prototype.wheels = 4; // 프로토타입에 바로 매핑할 수 있습니다.

const x5 = new Bmw("red");
const z4 = new Bmw("blue");

console.log(x5.wheels) // 4

 

instanceof 메서드를 이용하면 객체가 인스턴스로 포함되어 있는 지 불린형으로 반환됩니다.

하기 코드 기준으로 z4가 Bmw 생성자 함수에 인스턴스인 경우 true를 반환합니다.

const Bmw = function(color) {
  this.color = color;
}

Bmw.prototype.wheels = 4;

const x5 = new Bmw("red");
const z4 = new Bmw("blue");

console.log(z4 instanceof Bmw) // true

 

또는 객체의 constructor 프로퍼티를 이용하여 생성자 함수를 찾을 수 있습니다.

const Bmw = function(color) {
  this.color = color;
}

Bmw.prototype.wheels = 4;

const x5 = new Bmw("red");
const z4 = new Bmw("blue");

console.log(z4.constructor === Bmw) // true

 

생성자 함수에 추가 할 프로토타입이 여러 개인 경우 json 형식으로 추가할 수 있습니다.

그러나 생성자 함수를 비교할 때 false를 반환합니다.
(이러한 특성 때문에 가급적 하나로 묶지 말고 개별로 추가하는 것을 권장합니다.)

단, 인스턴스에 속해있는 지 판단 여부를 확인 할 수는 있습니다.

const Bmw = function(color) {
  this.color = color;
}

Bmw.prototype = {
  wheels: 4,
  drive() {
    console.log("drive!!")
  },
  navigation: 1
 };

const x5 = new Bmw("red");
const z4 = new Bmw("blue");

console.log(z4.constructor === Bmw) // false
console.log(z4 instanceof Bmw) // true

 

json 형태로 묶어서 추가하고 싶은 경우 constructor 프로퍼티를 수동으로 넣어주는 방법도 있습니다.

const Bmw = function(color) {
  this.color = color;
}

Bmw.prototype = {
  constructor: Bmw,
  wheels: 4,
  drive() {
    console.log("drive!!")
  },
  navigation: 1
 };

const x5 = new Bmw("red");
const z4 = new Bmw("blue");

console.log(z4.constructor === Bmw) // true
console.log(z4 instanceof Bmw) // true

 

상속 받은 인스턴스를 통해서 값을 쉽게 변경할 수 있습니다.

그러나, 이는 의도치 않은 사람이 해당 값을 변경할 수 있게 해주기 때문에 보안성 측면에서 전혀 도움이 되지 않습니다.

const Bmw = function(color) {
  this.color = color;
}

const x5 = new Bmw("red");

x5.color = "black" // red -> black 변경

console.log(x5.color) // black

 

 

이럴 때는 이전에 정리했던 클로저(https://baobab.live/113)를 이용해야 합니다.

color 값은 오직 생성할 때 입력된 컨텍스트로만 출력됩니다.

const Bmw = function(color) {
  const c = color;
  this.getColor = function() { // 
    console.log(c);
  }
}

const x5 = new Bmw("red");
x5.color = "black"
x5.getColor(); // red

 


Reference

'Tech > [Lang] JS & TS' 카테고리의 다른 글

promise에 대해서  (0) 2021.10.12
클래스  (0) 2021.10.07
함수 호출 메서드 (call, apply, bind)  (0) 2021.09.27
스케쥴링 메서드 (setTimeout, setInterval)  (0) 2021.09.24
클로저와 어휘적 환경  (0) 2021.09.23