모카스터디/JavaScript

프로토타입[Prototype] 과 클래스(Class)

softmoca__ 2024. 3. 1. 14:57
목차

프로토타입[Prototype]

 let user = {
     name: 'John',
     age: 45,
     email: 'john@example.com'
 }

 console.log(user.name);
 console.log(user.hasOwnProperty('email'));

 

위 코드에서 user라는 객체에는 hasOwnProperty라는 메서드가 없지만 사용이 가능하다.

그럼 hasOwnProperty는 어디서 온걸까 ?

 

 

 

위 사진과 같이 모든 객체는 global Object prototype을 가진다.

 

그렇다면 prototype은 무엇일까 ?

프로토타입은 자바스크립트 객체 다른 객체로부터 메서드와 속성을 상속받는 메커니즘을 말한다.

이것을 프로토타입 체인(prototype chain)이라고도 말한다.

위에서 보듯이 Prototype Object 안에 있는 hasOwnProperty 를 상속받아서 사용하고 있다.

이렇게 하므로 인해서 더 적은 메모리를 사용할 수가 있고 코드를 재사용 할수 있다.

 

 

 function Person(name, email, birthday) {
     this.name = name;
     this.email = email;
     this.birthday = new Date(birthday);
     this.calculateAge = function () {
     const diff = Date.new() - this.birthday.getTime();
     const ageDate = new Date(diff);
     return Math.abs(ageDate.getUTCFullYear() - 1970);
 	}
 }
 
 
 const john = new Person('john', 'john@example.com', '7-10-91');
 const han = new Person('han', 'han@example.com', '2-11-91');
 console.log(john);
 console.log(han);

위 코드는 생성자(constructor) 함수를  사용해서 다른 객체들을 만든 예시이다.

그럼 아래와 같이 birthday와 email,name의 값은 다르지만  calculateAge의 함수는 같다.!

그러니 프로토 타입으로 옮겨서 상속을 통해 사용하는 것이 더 효율적이다.

 

 

프로토 타입으로 함수를 옮긴 예시 코드 1

 function Person(name, email, birthday) {
     this.name = name;
     this.email = email;
     this.birthday = new Date(birthday);

 }

 Person.prototype.calculateAge = function () {
     const diff = Date.new() - this.birthday.getTime();
     const ageDate = new Date(diff);
     return Math.abs(ageDate.getUTCFullYear() - 1970);
 }

 const john = new Person('john', 'john@example.com', '7-10-91');
 const han = new Person('han', 'han@example.com', '2-11-91');
 console.log(john);
 console.log(han);

 

 

 

프로토 타입으로 함수를 옮긴 예시 코드 2

Object.create() 메서드는 지정된 프로토타입 객체 및 속성을 갖는 새 객체를 만든다.

function Person(name, email, birthday) {
    const person = Object.create(personsPrototype);
    person.name = name;
    person.email = email;
    person.birthday = new Date(birthday);
    return person;
}

const personsPrototype = {
    calculateAge() {
        const diff = Date.new() - this.birthday.getTime();
        const ageDate = new Date(diff);
        return Math.abs(ageDate.getUTCFullYear() - 1970);
    }
}

const john = new Person('john', 'john@example.com', '7-10-91');
const han = new Person('han', 'han@example.com', '2-11-91');
console.log(john);
console.log(han);

 

그럼 이제 매번 객체를 생성할 때 마다 calculateAge메서드를 생성하는 것이 아닌 프로토 타입에서 꺼내서 사용하면 되기 때문에 재사용성이 상당히 올라간다.

 

 

 

 

클래스(Class)


ES6에서 나온 Class를 이용해서 더 쉽게 OOP을 구현할 수 있다. 

OOP 방식을 이용하지만 내부에서 prototype을 사용하며 작동된다.

 

Constructor

constructor(생성자)를 사용하면 인스턴스화된 객체에서 다른 메서드를 호출하기 전에 수행해야 하는 사용자 지정 초기화를 제공할 수 있다.

클래스를 new 를 붙여서  ( new User("John") ) 인스턴스 객채로 생성하면 넘겨받은 인수와 함께 constructor가 먼저 실행된다.

이 때 넘겨받은 인자인 John이 this.name 에 할당된다.

 

 

 constructor는 인스턴스의 생성과 동시에클래스 필드의 생성과 초기화를 실행한다.

- this는 클래스가 생성할 인스턴스를 가리킨다.

- constructor은 생략할 수 있다.

- constructor()는 new에 의해 자동으로 호출된다.

 class Person {
     constructor(name, email, birthday) {
         this.name = name;
         this.email = email;
         this.birthday = new Date(birthday);
     }

     introduce() {
         return `Hello my name is ${this.name}`;
     }
 }

 const john = new Person('john', 'john@example.com', '10-3-98');
 console.log(john);

 

 

 

그럼 자동으로 introduce메서드는 프로토 타입에 들어간다.

 

 

Static

"prototype"이 아닌 클래스 함수 자체에 메서드를 설정할 수도 있다. 

이런 메서드를 정적(static) 메서드라고 부른다. 

 

constructor생성자 내부의 필드(name,email,birthday)를 사용하지 않고 독립적인 것을 정의할 때 static을 사용하며 이 static 메서드를 사용할 때는 인스턴스가 아닌 클래스 이름을 이용해서 사용한다.

 

즉, 클래스에만 남아 있고 각각의 인스턴스에는 없다 !

 class Person {
     constructor(name, email, birthday) {
         this.name = name;
         this.email = email;
         this.birthday = new Date(birthday);
     }

     introduce() {
         return `Hello my name is ${this.name}`;
     }

     static multipleNumbers(x, y) {
         return x * y;
     }

 }

 const john = new Person('john', 'john@example.com', '10-3-98');
 console.log(john);

 

프로토 타입 내부의 constructor 자체가 해당 클레스 자체이다.

 

 

Sub Class

부모 클래스를 자식 클래스에 확장할 수 있다.

부모 클래스에 있던 기능을 토대로 자식 클래스를 만들 수 있다.

그렇게 하기 위해서는 extends 키워드를 사용해주면 된다.

 

class Person {
    constructor(name, email) {
        this.name = name;
        this.email = email;
    }

    introduce() {
        return `Hello my name is ${this.name}`;
    }
}

class Client extends Person {
    constructor(name, email, phone, address) {
        super(name, email)

        this.phone = phone;
        this.address = address;
    }
}

const john = new Client('John', 'john@example.com', '010-0000-0000', '서울');
console.log(john.introduce());

부모 클래스에게 상속받아 자식 클래스를 만들고, 자식 클래스에 부모 클래스의 속성을 불러올 때 super()를 사용한다.

 

john.introduce가 실행되는 순서 

1. client 객체에 client.introduce 가 있는지 확인한다. 

2. 없기 때문에 Client.prototype에 있는지도 확인하지만 introduce는 없다. 

3. extends를 통해 관계가 만들어진 Client.prototype의 프로토타입인 Person.prototype에 메서드가 있는지 확인한다.  여기에 introduce가 있기 때문에 이것을 사용한다.

 

super()

- 자식 클래스 내에서 부모 클래스의 생성자를 호출할 때 사용된다.

- 자식 클래스 내에서 부모 클래스의 메소드를 호출할 때 사용된다.

class Car {
  constructor(brand) {
    this.carname = brand;
  }
  present() {
    return "I have a " + this.carname;
  }
}

class Model extends Car {
  constructor(brand, mod) {
    super(brand);
    this.model = mod;
  }
  show() {
    return super.present() + ", it is a " + this.model;
  }
}

let mycar = new Model("Ford", "Mustang");
mycar.show()

 

super([arguments]);                   // 부모 생성자 호출

super.functionOnParent([arguments]);   // 부모 메서드 호출