[Javascript] 프로토타입과 클래스

SNXWXH·2024년 11월 4일

Javascript

목록 보기
11/13
post-thumbnail

JavaScript는 prototype기반의 객체지향 프로그래밍 언어이지만
ES6부터 추가된 class는 직관적으로 쉽게 코드를 읽을 수 있게 만들어 줄 수 있다.

Prototype

function Me(name) {
  this.name = name;
}

Me.prototype.wow = function () {
  console.log("WOW!");
};

let person = new Me("Jason");
person.wow(); // WOW!

Class

class Me {
  constructor(name){
    this.name = name;
  }

  wow(){
    console.log("WOW!");
  }
}

let person = new Me("Jason");
person.wow() // WOW!

프로토타입

어떤 생성자 함수를 new 연산자와 함께 호출하면 Constructor에서 정의된 내용을 바탕으로 새로운 인스턴스 생성한다.
인스턴스에는 proto라는 프로퍼티가 자동으로 부여되고 이는 Constructor의 prototype이라는 프로퍼티 참고한다.

//__proto__가 생성자의 prototype 프로퍼티를 참조

const person = new Person('will');
Person.prototype === person.__proto__ //true

new 연산자로 Constructor를 호출하면 인스턴스가 만들어지는데, 이 인스턴스의 생략 가능한 프로퍼티인 proto는 Constructor의 prototype을 참조한다.

이는 생성자 함수의 prototype에 어떤 메소드나 프로퍼티가 있다면 인스턴스에서도 메소드나 프로퍼티에 접근할 수 있다는 뜻이다.

__proto__

__proto__는 접근하고자 하는 객체의 내부 속성인 prototype을 노출하는 접근자 속성 함수이다.

모든 객체는 __proto__로 통해 자신의 프로토타입에 접근할 수 있다.

그러나 내부 슬롯에 직접적으로는 접근이 불가하다. 그 이유는 프로토타입 체인의 단방향을 지키기 위해서다.

클래스

클래스는 ES6부터 추가된 문법으로, 객체 생성자로 구현했던 코드를 깔끔하게 구현할 수 있도록 도와주며 상속도 쉽게 할 수 있다.

객체를 직접 작성하여 정의하고 생성할 수도 있지만 클래스로 만들어주면 여러 객체를 더 쉽게 만들 수 있다.

클래스를 통해 원하는 구조의 객체 틀을 짜놓고 비슷한 모양의 객체를 공장처럼 찍어낼 수 있다.

쉽게 생각해서 클래스 === 붕어빵 기계객체 ==== 붕어빵으로 보면 된다.

// constructor()메소드에서 2개의 매개변수 namer과 age로 
// Korean 인스턴스의 name, age 프로퍼티에 값을 할당

class Korean {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this.country = 'Korea';
  }

  addAge(age) {
    return this.age + age;
  }
}
  • 클래스 내에 정의된 함수를 method라 부름

  • 클래스를 통해 생성된 객체를 인스턴스(instance)라고 부름

  • 클래스도 함수처럼 호출하기 전까지는 코드가 실행되지 않음

    ⇒  new키워드와 소괄호()를 사용하여 호출

  • 클래스 이름은 대문자로 시작

  • constructor는 class에서 필요한 기초 정보를 세팅하는 곳

    ⇒ 객체를 new로 생성할 때 가장먼저 자동으로 호출

  • 클래스에서 this는 본인 객체를 의미

    ⇒ 클래스 내에서 메소드끼리 소통하기 위해서는 this가 필요

// Korean 클래스를 이용해 kim 객체를 만들면 아래와 같이 instance가 생성

let kim = new Korean("KIMJINYOUNG",24);

{
  name: 'KIMJINYOUNG',
  age: 24,
  country: 'Korea',
  addAge: function(age){
	return this.age + age;
  }
}

상속

extends

class Cat {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    eat() {
        return `${this.name} is eating!`}
    meow() {
        return 'MEOWWWW'
    }
}

class Dog {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    eat() {
        return `${this.name} is eating!`}
    bark() {
        return 'WOOF'
    }
}

두개의 클래스를 보면 조금은 다르지만 복제된 코드들이 많다.

JS에서는 이렇게 복제된 코드를 하나로 관리하여 두 개의 클래스에서 사용할 수 있다.

그렇게 되면 하나로 관리하는 곳은 부모, 해당 코드를 사용하는 곳은 자식이 된다.

이때 사용할 수 있는 키워드가 extend 이다.

class Pet {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    eat() {
        return `${this.name} is eating!`}
}

class Cat extends Pet {
    meow() {
        return 'MEOWWWW'
    }
}

class Dog extends Pet {
    bark() {
        return 'WOOF'
    }
}

이처럼 extend 키워드를 사용하면 자식 클래스인 Dog에서 부모 클래스인 Pet의 값을 사용할 수 있다.

let boksil = new Dog("Boksil",5);

boksil.eat() // Boksil is eating!

Dog 클래스를 보면 eat 메소드와 constructor 메소드가 없는데도 eat 메소드를 사용할 수 있다.

이는 프로토타입에서 eat을 찾지 못하면 프로토타입 체이닝을 통해 부모의 class에서 해당 메소드를 찾는다.

class Dog extends Pet {
    bark() {
        return 'WOOF'
    }
    eat() {
        return `${this.name} is happy!`}
}

let boksil = new Dog("Boksil",5);

boksil.eat() // Boksil is happy!

Dog에도 eat 메소드를 추가해 준다면 제일 가까이에 있는 Dog의 eat을 출력하게 된다.

super

부모 클래스의 값을 상속받고, 추가적으로 자식만의 값을 사용하고싶다면 super 키워드를 사용할 수 있다.

class Pet {
    constructor(name, age) {
        console.log('IN PET CONSTRUCTOR!')
        this.name = name;
        this.age = age;
    }
    eat() {
        return `${this.name} is eating!`}
}

class Cat extends Pet {
    constructor(name, age, livesLeft = 9) {
        console.log('IN CAT CONSTRUCTOR!')
        super(name, age)
        this.livesLeft = livesLeft;
    }
    meow() {
        return 'MEOWWWW'
    }
}

const monty = new Cat('monty',9);
// IN CAT CONSTRUCTOR!
// IN PET CONSTRUCTOR!

console.log(monty); // Cat {name: 'monty', age: 9, livesLeft: 9}

monty를 살펴보면 name이 monty이고, age는 9살에 livesLeft는 9로 설정되어 있다.

super 키워드를 사용해 부모 클래스의 constructor (Pet의 생성자)에서 온 기능을 재사용했지만, Cat에 따로 하나를 추가한 것이다.

추가 메소드

  • set : 값을 설정하는 키워드

  • get : 값을 가져오는 키워드

    class Car {
      constructor(speed) {
        this.speed = speed;
      }
    
      get speed() {
        return this._speed;
      }
    
      set speed(value) {
        this._speed = value < 0 ? 0 : value;
      }
    
      getSpeed() {
        return this.speed;
      }
    
        // ...
    }
    const car1 = new Car(-100);
    
    console.log(car1.getSpeed());
  • static

    class MathUtils {
      static APP_NAME = "Math Utils";
      static PI = 3.141592653589793;
    
      constructor() {}
    
      static add(a, b) {
        return a + b;
      }
    
      static minus(a, b) {
        return a - b;
      }
    }
    const mathUtils = new MathUtils();
    
    console.log(MathUtils.PI);
    console.log(MathUtils.add(10, 20));
    console.log(MathUtils.minus(10, 20));
  • private(#)

    class Car {
      #speed;
      constructor(speed) {
        this.#speed = speed;
      }
      getSpeed() {
        return this.#speed;
      }
    }
    const car1 = new Car(100);
    
    console.log(car1.getSpeed());

📍참고

profile
세상은 호락호락하지 않다. 괜찮다. 나도 호락호락하지 않으니까.

0개의 댓글