도라에몽 개발자

클래스(Class); 프로토타입(Prototype) 본문

LANGUAGE/JavaScript

클래스(Class); 프로토타입(Prototype)

Doraemon_lulu 2023. 12. 26. 23:58

프로토타입(Prototype) vs 클래스(Class)

"JavaScript는 프로토타입 기반의 언어(prototype-based language)이다."

 

 JavaScript 또한 객체지향 언어이나 Java와 같은 객체지향 언어와는 달리, 클래스(Class) 대신 프로토타입(Prototype)이 존재함. 

■ 모든 객체들이 메소드와 속성들을 상속 받기 위한 템플릿으로써 프로토타입 객체(prototype object)를 가진다는 의미임.

 함수(Function)로 객체(Object)를 생성하여 클래스(Class)처럼 사용하게 됨.   

 

*** 함수로 객체를 생성하여 해당 함수 내에서 생성자(constructor)를 선언한 후, new 키워드를 사용하여 해당 함수에 대한 프로토타입 객체 생성 및 연결 가능함. 

class A { // 클래스(class) A 선언
    constructor() {} // 생성자(constructor) 구문
}

 

 ▼ 클래스(class) 개념이 없기 때문에 상속 기능 또한 없으나, 프로토타입을 기반으로 상속 기능을 구현하여 사용함을 참고할 것.

 → extends 키워드 사용하여 클래스 간의 상속 관계 만들어줌.

// 상속(inheritance)
class A {
    constructor() {}
}
class B extends A { // A 클래스 - 부모, B 클래스 - 자식
    constructor() {
        super()
    }
}
class C extends B { // B 클래스 - 부모, C 클래스 - 자식
    constructor() {
        super()
    }
}

 

prototype

Array 

  • 정의
    - 다른 프로그래밍 언어의 배열과 마찬가지로, Array 객체는 여러 항목의 컬렉션을 단일 변수 이름 아래 저장할 수 있고 일반적인 배열 연산을 수행하기 위한 멤버가 있음.
    - prototype 속성에 연결된 메서드를 주로 활용함.

[참고]

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array

 

Array - JavaScript | MDN

다른 프로그래밍 언어의 배열과 마찬가지로, Array 객체는 여러 항목의 컬렉션을 단일 변수 이름 아래 저장할 수 있고, 일반적인 배열 연산을 수행하기 위한 멤버가 있습니다.

developer.mozilla.org

 

  • Array 인스턴스의 prototype 속성의 메서드 종류
    Array.prototype.___

    - length() 메서드: 배열의 길이 반환함.
    - includes() 메서드: 배열의 항목에 특정 값이 포함되어 있는지 판단하고, true/false 반환함. 
    ...

▼ Array 인스턴스 및 prototype 속성인 length() 및 includes() 메서드 활용 예시

// 생성자 함수 new Array 로부터 할당된 인스턴스(instance) fruits는 다음과 같음.
const fruits = new Array('Apple', 'Banana', 'Cherry')

console.log(fruits) // ['Apple', 'Banana', 'Cherry']
console.log(fruits.length) // 3
console.log(fruits.includes('Banana')) // true
console.log(fruits.includes('Strawberry')) // false

Array.prototype.JJack = function () { // 인위적으로 JJack이라는 이름의 메서드 생성
    console.log(this)
}

fruits.JJack() // ['Apple', 'Banana', 'Cherry']

const arr = [] // 새로운 배열 arr 생성
arr.JJack()

 

다른 함수에서 선언한 메서드를 빌려서, 원하는 결과를 출력하는데 사용한 예시

// jjack 함수 생성
const jjack = {
    firstName: 'JJack', 
    lastName: 'Sparrow',
    getFullName() {
        return `${this.firstName} ${this.lastName}`
    }
}

// mark 함수 생성
const mark = {
    firstName: 'Mark',
    lastName: 'Lee',
    // getFullName() {
    //     return `${this.firstName} ${this.lastName}`
    // }
}

console.log(jjack.getFullName())
/* mark 함수에서 중복의 getFullName 메서드를 선언하지 않고, 
jjack 함수의 getFullName 메서드를 빌려서 원하는 결과를 출력하는 방법은 다음과 같음. */
console.log(jjack.getFullName.call(mark))

 

위의 예시와는 달리, prototype 속성의 메서드를 선언하는 방법을 통해 공통으로 사용되는 메서드를 만들어 공유하는 방법의 예시

function User(first, last) {
    this.firstName = first
    this.lastName = last
}

// prototype 속성 활용하여 공통적으로 사용할 getFullName과 같은 메서드 정의함. 
User.prototype.getFullName = function () {
    return `${this.firstName} ${this.lastName}`
}

// new User: 생성자 함수
const jjack = new User('JJack', 'Sparrow') // JJack, Sparrow 인자를 가진 새로운 객체(jjack) 생성
const mark = new User('Mark', 'Lee') // Mark, Lee 인자를 가진 새로운 객체(mark) 생성

console.log(jjack) // User {firstName: 'JJack', lastName: 'Sparrow'}
console.log(mark)  // User {firstName: 'Mark', lastName: 'Lee'}
console.log(jjack.getFullName()) // JJack Sparrow 
console.log(mark.getFullName())  // Mark Lee

 

ES6 Class 기본 문법

2015년에 발표된 JavaScript의 새로운 문법

// function User(first, last) {
//     this.firstName = first
//     this.lastName = last
// }

// User.prototype.getFullName = function () {
//     return `${this.firstName} ${this.lastName}`
// }

/* 위와 같은 내용을 클래스 형식으로 변환한 결과는 다음과 같음. 
User 클래스 내에서 first, last 인자를 포함한 생성자와 getFullName 메서드 생성함. */
class User {
    constructor (first, last) {
        this.firstName = first
        this.lastName = last
    }
    getFullName() {
        return `${this.firstName} ${this.lastName}`
    }
}

const jjack = new User('JJAck', 'Sparrow')
const mark = new User('Mark', 'Lee')

// 객체 데이터가 생성되어 다음과 같은 결과가 출력됨.
console.log(jjack) // User {firstName: 'JJAck', lastName: 'Sparrow'}
console.log(mark) // User {firstName: 'Mark', lastName: 'Lee'}

 

Getter, Setter 

  • Getter
    - 값을 얻어내는(조회하기 위한) 용도의 메서드
    - get 메서드명() { ... 리턴문 }

  • Setter
    - 값을 지정하는 용도의 메서드
    - set 메서드명(변수명) { ... }
// User 클래스
class User {
    constructor(first, last) {
        this.firstName = first
        this.lastName = last
    }
    // getter: 값을 얻어내는 용도의 메서드 
    get fullName() {
        console.log('Getting full name!')
        return `${this.firstName} ${this.lastName}`
    }
    // setter: 값을 지정하는 용도의 메서드
    set fullName(value) {
        console.log(value)
        ;[this.firstName, this.lastName] = value.split(' ') // value.split(' '): 띄어쓰기를 기준으로 구분하여 배열 데이터에 저장해줌. 
    }
}

// User 함수를 new 키워드로 호출하는 것을 생성자 함수라고 함. 
// 생성자 함수로 JJack, Sparrow 인자를 포함시켜 jjack 변수에 할당함.
const jjack = new User('JJack', 'Sparrow')
console.log(jjack.fullName)

jjack.firstName = 'Mark'
console.log(jjack.fullName) // firstName 값은 변하여 출력되나, fullName은 변하지 않은 상태로 출력됨.

// 할당 연산자를 사용하여 값을 지정하기 때문에 Setter 실행됨.
jjack.fullName = 'Mark Lee' // value라는 매개변수가 데이터를 받아서 fullName의 값으로 할당함.
console.log(jjack) // User {firstName: 'Mark', lastName: 'Lee'}

 

정적 메소드(Static methods)

  • prototype 메소드
    - Array.prototype.메소드명()
    - 배열 데이터와 관련된 속성을 가진 메소드

  • 정적 메소드
    - Array.isArray()
    - 인자가 Array (배열)인지 여부를 판별함.
    - 출력 결과가 true / false 로 출력됨. 
Array.isArray([1, 2, 3]); // true
Array.isArray({ foo: 123 }); // false (배열이 아닌, 객체이기 때문)
Array.isArray("foobar"); // false (배열이 아닌, 문자열이기 때문)
Array.isArray(undefined); // false (배열이 아닌, undefined 이기 때문)
class User {
    constructor(first, last) {
        this.firstName = first
        this.lastName = last
    }
    getFullName() {
        return `${this.firstName} ${this.lastName}`
    }
    // 정적 메소드 isUser 생성 -> 인스턴스가 아닌, 클래스로 직접 접근하여 사용해야 함.
    static isUser(user) {
        if (user.firstName && user.lastName) {
            return true
        }
        return false
    }
}

const jjack = new User('JJack', 'Sparrow')
const mark = new User('Mark', 'Lee')
const taemin = {
    name: 'Taemin Lee',
    age: 30
}

console.log(jjack.getFullName()) // JJack Sparrow
console.log(mark.getFullName())  // Mark Lee
// console.log(User.getFullName()) // User 클래스에서 메소드를 바로 호출할 수 없고, 인스턴스(jjack, mark) 통해 호출해야 함. 
console.log(User.isUser(jjack))  // true
console.log(User.isUser(taemin)) // false

 

상속(inheritance) & instanceof 

상속(inheritance) 

- 상속받는 클래스명 extends 상속해주는 클래스명
- 상속을 해주는 클래스의 내용을 상속 받는 클래스가 상속 받아 사용할 수 있음.

instanceof

- 상속받았는지 확인하고자 하는 클래스명 instanceof 상속해준 클래스인지 확인하고자 하는 클래스명
- 상속 여부 확인함.

class Vehicle {
    constructor(acceleration = 1) {
        this.speed = 0
        this.acceleration = acceleration
    }
    accelerate() {
        this.speed += this.acceleration
    }
    decelerate() {
        if (this.speed <= 0) {
            console.log('정지!')
            return
        }
        this.speed -= this.acceleration
    }
}

class Bicycle extends Vehicle { // Bicycle 클래스는 Vehicle 클래스 내용을 상속 받아서 사용함.
    constructor (price = 100, acceleration) {
        super(acceleration) // super 함수를 통해 상속 받은 내용: Vehicle의 acceleration
        this.price = price
        this.wheel = 2
    }
}

const bicycle = new Bicycle(300)
bicycle.accelerate()
bicycle.accelerate()
console.log(bicycle)
console.log(bicycle instanceof Bicycle)
console.log(bicycle instanceof Vehicle)

class Car extends Bicycle {
    constructor(liscense, price, acceleration) {
        super(price, acceleration)
        this.liscense = liscense
        this.wheel = 4
    }
    // Overriding (오버라이딩)
    accelerate() {
        if (!this.liscense) {
            console.error('무면허!') // error 표시로 출력
            return
        }
        this.speed += this.acceleration
        console.log('가속!', this.speed)
    }
}

const carA = new Car(true, 7000, 10)
const carB = new Car(false, 4000, 6)
carA.accelerate()
carA.accelerate()
carB.accelerate()

console.log(carA instanceof Bicycle)
console.log(carB instanceof Vehicle)

class Boat extends Vehicle {
    constructor(price, acceleration) {
        super(acceleration)
        this.price = price
        this.motor = 1
    }
}

// instanceof 활용하여 상속 여부 확인 (true/false)
const boat = new Boat(10000, 5) 
console.log(boat instanceof Bicycle) // false
console.log(boat instanceof Car) // false
console.log(boat instanceof Vehicle) // true

 

instanceof & constructor (생성자) 활용 방법

class A {
    constructor() {}
}
class B extends A { // B 클래스는 A 클래스로부터 상속 받음.
    constructor() {
        super()
    }
}
class C extends B { // C 클래스는 B 클래스로부터 상속 받음.
    constructor() {
        super()
    }
}

// 인스턴스(instance) a, b, c 생성 (cf. 생성자 함수 A, B, C)
const a = new A()
const b = new B()
const c = new C()

// A 클래스는 B, C 클래스의 부모 클래스이고, B 클래스는 C 클래스의 부모 클래스임.
console.log(c instanceof A) // 인스턴스 c는 A 클래스로부터 상속 받았는지 확인 (true)
console.log(c instanceof B) // 인스턴스 c는 B 클래스로부터 상속 받았는지 확인 (true)
console.log(c instanceof C) // 인스턴스 c는 C 클래스로부터 상속 받았는지 확인 (true)

console.log(c.constructor === A) // 인스턴스 c의 생성자가 A 클래스와 생성자와 동일한지 확인 (false)
console.log(c.constructor === B) // 인스턴스 c의 생성자가 B 클래스의 생성자와 동일한지 확인 (false)
console.log(c.constructor === C) // 인스턴스 c의 생성자가 C 클래스의 생성자와 동일한지 확인 (true)

const fruits = ['Apple', 'Banana'] // Array (배열)
// const fruits = new Array('Apple', 'Banana') // 위와 동일한 결과를 가져옴.
console.log(fruits.constructor === Array) // true
console.log(fruits instanceof Array) // true

'LANGUAGE > JavaScript' 카테고리의 다른 글

표준 내장 객체  (1) 2023.12.29
함수  (1) 2023.12.23
연산자와 구문  (1) 2023.12.22
Node.js 설치 시 참고사항  (0) 2023.12.21
JS 데이터  (1) 2023.12.21