JavaScript | 상속, 프로토타입(Prototype)

Kate Jung·2022년 2월 14일
0

JavaScript

목록 보기
29/39
post-thumbnail

📌 hasOwnProperty()

객체가 특정 프로퍼티를 (직접) 가지고 있는지 확인 (boolean)

🔹 사용법

객체명.hasOwnProperty('확인할 프로퍼티 이름')

🔹 예시 코드

const user = {
    name: "Mike"
}

user.name // 'Mike'

user.hasOwnProperty('name') // true
user.hasOwnProperty("age") // false

🔹 위치

  • [[Prototype]] (__proto__) 라는 객체가 있음. 이것을 프로토 타입이라고 함.
  • 객체에서 프로퍼티를 읽으려고 하는데 없으면 여기에서 찾음.

🔹 객체 내부에 hasOwnProperty 존재 시

만든 메소드가 동작함.

  • 일단 객체에 그 프로퍼티가

    • 있으면 → 거기서 탐색 중지.

    • 없을 때만 → 프로토타입에서 프로퍼티를 찾음.

  • 예시 코드

    const user = {
        name: "Mike",
        hasOwnProperty: function () {
            console.log("haha")
        }
    }
    
    user.hasOwnProperty() // 'haha'

📌 상속

  • 프로토타입이 어떻게 동작하는지 보기 위해 ‘상속’ 이용

🔹 순서 & 원리

  1. 상위 개념 객체 생성

    공통된 부분들이 해당됨.

  2. 기존 객체들에서 공통 부분 지우기

  3. [상속] 객체명.__proto__ = 상위 개념 객체명 작성

    상위 개념 객체가 해당 객체의 프로토타입이 됨.

    == 해당 객체는 상위 개념 객체의 상속을 받음.

  4. ‘(상속 받은) 프로퍼티 사용하는 경우’ 원리

    1. 객체명.프로퍼티 작성

    2. 객체 내부에서 프로퍼티를 찾음

    3. 찾으면 → 탐색 중지

      없다면 → __proto__ (상속받은 프로퍼티 有) 확인

🔹 예시 코드

◽ 문제

const bmw = {
  color: "red",
  wheels: 4,
  navigation: 1,
  drive(){
    console.log('...drive')
  },
}

const benz = {
  color: "black",
  wheels: 4,
  drive(){
    console.log('...drive')
  }
}

const audi = {
  color: "blue",
  wheels: 4,
  drive(){
    console.log('...drive')
  }
}
  • 공통된 부분

    • wheels, drive

      → 문제 : 차들이 늘어나면 계속 새로운 변수로 만들어짐.

    • 처리 방법

      __proto__로 해결 가능

◽ 해결

// 1. 상위 개념 객체(car) 생성
const car = {
  wheels: 4, // 👈 공통된 프로퍼티들
  drive(){
    console.log('...drive')
  },
}

// 2. 공통 부분 (wheels, drive) 지움.
const bmw = {
  color: "red",
  navigation: 1,
}

const benz = {
  color: "black",
}

const audi = {
  color: "blue",
}

// 3. 상속
bmw.__proto__ = car; 
benz.__proto__ = car;
audi.__proto__ = car;

// 4. [확인] (상속 받은) 프로퍼티 사용
bmw // {color: 'red', navigation: 1} 밖에 안 나옴.
bmw.color // 'red'
bmw.wheels // 4 (상속 받은 프로퍼티 사용)

🔹 Prototype Chain

  • 상속은 계속 이어질 수 있다.

  • Prototype Chain 이란?

    특정 객체의 프로퍼티 or 메소드에 접근 시, 객체 자신의 것뿐 아니라 __proto__가 가리키는 링크를 따라서 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티 or 메소드에 접근 가능

◽ 예시 코드

const car = {
  wheels: 4,
  drive(){
    console.log('...drive')
  },
}

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

bmw.__proto__ = car;

const x5 = {
  color: "white",
  name: "x5",
}

x5.__proto__ = bmw;

x5.name // 'x5'
x5.color // 'white' (이유: x5에 color가 있으니 여기까지 탐색 후, 값 주고 멈춤.)
x5.navigation // 1

  • 원리

    • navigation

      x5에서 찾고 없으니까

      prototypebmw에서 탐색

      → 있으니까 멈춤.

    • drive()

      x5에 없고

      bmw로 올라가봐도 없고

      prototypecar까지 올라가서야 사용 가능

🔹 객체 프로퍼티 순회

◽ for in 활용

모두 나옴.

  • 기본 예시

    // Prototype Chain 예시 코드 활용
    
    for (p in x5) {
      console.log(p)
    }
    /*
    - 결과:
    'color'
    'name'
    'navigation'
    'wheels'
    'drive'
    
    - 설명:
    name, color 제외하고 프로토타입에서 정의한 프로퍼티들임. */
  • 객체가 직접 가지고 있는지 구분하고 싶다면

    hasOwnProperty() 활용

    for (p in x5) {
      if (x5.hasOwnProperty(p)){
        console.log('o',p)
      } else {
        console.log('x',p)
      }  
    }
    /*
    'o' 'color'
    'o' 'name'
    'x' 'navigation'
    'x' 'wheels'
    'x' 'drive'
    */

◽ 키, 값 관련 객체 내장 메소드

상속된 프로퍼티 → 안 나옴.

// Prototype Chain 예시 코드 활용

Object.keys(x5) // [ 'color', 'name' ]
Object.values(x5) // [ 'white', 'x5' ]

🔹 생성자 함수 이용

생성자 함수 이용 시, 비슷한 객체들을 간단히 제작 가능.

◽ 예시

const Bmw =function(color) {
  this.color = color;
  this.wheels = 4;
  this.drive = function(){
    console.log('drive..')
  }
}

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

◽ 리팩토링 1

  • 리팩토링 이유

    wheels, drive는 동일 → 분리 가능 (매개변수로 넘긴 color 제외)

const car = {
  wheels : 4,
  drive(){
    console.log('drive..')
  }
}

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

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

x5.__proto__ = car;
z4.__proto__ = car;

/* 확인 */
x5 // Bmw {color: 'red'}
x5.wheels // 4

◽ 리팩토링 2

  • 리팩토링 이유

    prototype 이용 시, 중복 코드 줄이기 가능

    (한 번 작업 시, 생성자로 만들어진 모든 객체에 일일이 작업(객체명.__proto__ = 상위 개념 객체명)해줄 필요 無 (생성자 함수 사용 이유가 간편해서 인데))

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

Bmw.prototype.wheels = 4;
Bmw.prototype.drive = function() {
  console.log('drive..')
}
/*
기존의 __proto__와 모습이 다름.
이건 생성자 함수가 생성하는 객체의 __proto__를 wheels 이렇게 설정한다는 의미임.

그래서 x5.__proto__ = car;는 없어도 됨. */

// prototype에 몇 가지 더 추가
Bmw.prototype.navigation = 1;
Bmw.prototype.stop = function(){
  console.log("STOP!")
}

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

/* 확인 */
x5.wheels // 4
x5.drive() // drive..
x5.stop() // STOP!

🔶 instanceof

  • instance 란?

    생성자 함수가 새 객체를 만들어낼 때, 그 객체는 생성자의 instance

  • instanceof 란?

    해당 객체가 생성자로부터 생성된 것인지 판단 후, true / false 반환

    • 객체와 생성자 비교 가능
  • instance명.constructor

    해당 instance 객체 의 생성자를 가리킴.

    • 생성자로 만들어진 instance 객체에는 constructor라는 프로퍼티가 존재
  • 예시 코드

    z4 instanceof Bmw 
    /* 
    결과: true
    설명: z4는 Bmw로 생성됨. -> Bmw의 instance임.
    이렇게 하면 Bmw를 이용해서 z4를 만들었는지 알려줌. */
    
    z4.constructor === Bmw
    /*
    결과: true
    설명: 이렇게 생성자로 만들어진 instance 객체에는 constructor라는 프로퍼티가 존재함.
    constructor는 생성자 즉, Bmw를 가리킴. -> true가 나옴.
    */

◽ 리팩토링 3

JS는 명확한 constructor 보장 x. 개발자에 의해 수정 가능.

  • 문제

    const Bmw = function(color) {
      this.color = color;
    };
    
    Bmw.prototype = {  // 👈
      wheels: 4,
      drive() {
        console.log('drive..');
      },
      navigation: 1,
      stop(){
        console.log('STOP!')
      }
    }
    
    const x5 = new Bmw("red")
    const z4 = new Bmw("blue")
    
    /* 확인 */
    z4.constructor === Bmw // 👉 false
    • 문제점

      constructor 사라짐

  • 해결

    • [방법 1] 하나씩 프로퍼티 추가 (’리팩토링 2' 처럼)

      위 현상을 방지하기 위해서 prototype을 덮어 쓰지 말고 하나씩 프로퍼티를 추가하는 것이 좋음.

    • [방법 2] 수동으로 명시

      const Bmw = function(color) {
        this.color = color;
      };
      
      Bmw.prototype = {
        constructor: Bmw, // 👈 수동으로 명시
        wheels: 4,
        drive() {
          console.log('drive..');
        },
        navigation: 1,
        stop(){
          console.log('STOP!')
        }
      }
      
      const x5 = new Bmw("red")
      const z4 = new Bmw("blue")
      
      /* 확인 */
      z4.constructor === Bmw // true

◽ 클로저 활용

  • 문제

    const Bmw = function(color){
    	this.color = color;
    }
    
    const x5 = new Bmw("red")
    
    x5 // Bmw {color: 'red'}
    x5.color // 'red'
    
    /* 
    [문제점] 색상: 마음대로 변경 가능. 
    (but, 아무나 색을 바꾸면 안됨.) */
    x5.color = "black" // 'black' 
    x5.color // 'black'
  • 해결

    클로저 이용

    const Bmw = function(color){
    	const c = color;
      this.getColor = function(){
        console.log(c)
      }
    }
    
    const x5 = new Bmw("red")
    
    /* 확인 */
    x5.getColor();
    /*
    결과: red
    설명: 초기 세팅한 color 값을 얻을 수만 있고 바꿀 방법 無.
    color 함수는 생성될 당시의 context를 기억하는 것. */

참고

profile
복습 목적 블로그 입니다.

0개의 댓글