JavaScript 클래스

seul_velog·2021년 12월 11일
0

JavaScript

목록 보기
16/25
post-thumbnail
post-custom-banner

📍 1. 클래스(Class)란?

관련있는 변수나 함수들을 묶을때 사용하는 문법을 '클래스' 라고 한다. 클래스를 이용해서 상속과 다형성이 일어날 수 있는데, 이런 모든 것들이 가능한 것이 객체 지향 언어이다.

  • 기존의 prototype을 베이스로 하던 JS에 기반해서 그 위에 간편하게 쓸 수 있도록 class라는 문법만 추가된 것이며, 이런 것을 문법적 설탕(syntactical sugar)이라고 한다.

  • JavaScript 클래스는 JavaScript 객체용 템플릿이다. 비유하자면, 객체는 붕어빵이고 클래스는 붕어빵을 만드는 틀이라고 생각하면 된다.

  • 클래스를 사용하는 이유는 객체 단위로 코드를 그룹화 할 수 있고, 코드 재사용을 위해서 이다.

  • 클래스는 함수의 한 종류이다.

  • 키워드 class 를 사용하여 클래스를 만든다.



📌 인스턴스(instance)와 객체

일반적인 함수의 경우, 함수를 선언한 뒤 함수를 호출하여 동작시키듯이, 클래스의 경우엔 클래스 인스턴스를 생성해야 클래스에 들어 있는 내부 기능들을 사용할 수 있다.

let 인스턴스 = new 클래스이름();  // 일반적으로 인스턴스를 생성하는 방법
  • 객체(Object)는 인스턴스의 또 다른 이름으로, 상황에 따라 인스턴스 or 객체라고 부른다.
    인스턴스는 주로 new 연산자를 이용하여 클래스의 실체를 생성할 때 사용하고, 클래스에서 제공하는 프로퍼티와 메서드를 사용한다고 했을 때의 경우는 객체라고 말한다.





1-1. 기본적인 문법과 선언

기본문법

class ClassName {
  constructor() { ... }
}



클래스 생성 및 사용

예제

class Person {
    // constructor
    constructor(name, age){
        // fields
        this.name = name;  
        this.age = age;
        // this 라는 것은, this(생성된오브젝트).name 이라는 것이므로 'seul'이 출력되는 것.
    }
        
    // methods
    speak(){
        console.log(`${this.name}:hello!`);
    }
}

const my = new Person('seul', 15);

console.log(my.age);  // 15
console.log(my.speak());  // seul:hello! <- 이렇게 함수를 호출할 수 있다.

✍️ 생성과 호출 과정에 대해

  • 클래스 생성
    (1) class 라는 키워드를 이용해서 Person 이라는 클래스를 생성한다.
    (2) constructor 를 이용해서, 이후 오브젝트를 만들때 필요한 데이터를 전달한다.
    ( 예제의 클래스에는 두 가지의 fields와 methods가 존재한다. )
    (3) 전달받은 데이터를 이 클래스에 존재하는 두 가지의 fields(name, age)에 바로 할당 해준다.

  • 오브젝트 생성
    새로운 오브젝트를 만들 때는 new 라는 키워드를 쓴다.

  • new Person('seul', 15)를 호출하면 두가지 일이 발생한다.
    (1) 새로운 객체가 생성된다.
    (2) 넘겨받은 인수와 함께 constructor 가 자동으로 실행되고, 인수가 this.namethis.age 에 할당된다.

❗️ 클래스와 관련된 표기법은 객체 리터럴 표기법과 차이가 있다. 메서드 사이에는 쉼표가 없으니 주의하자. 쉼표를 넣으면 문법 에러가 발생한다.



클래스와 순수함수

클래스와 클래스 표현식, 그리고 동일한 기능을 하는 순수함수에 대해 비교해보자.

📌 클래스 키워드를 사용해서 선언 ▼

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

  sayHi() {
    alert(this.name);
  }
}

const my = new User("seul");
my.sayHi();

📌 클래스 역할을 하는 함수를 선언 ▼

// 생성자 함수를 만든다.
function User(name) {
  this.name = name;
}
// 모든 함수의 프로토타입은 'constructor' 프로퍼티를 기본적으로 갖고 있으므로
// constructor 프로퍼티를 명시적으로 만들 필요가 없다.

// property에 메서드를 추가한다.
User.prototype.sayHi = function () {
  alert(this.name);
};

let user = new User("lee");
user.sayHi();

✍️ 이 두 가지를 방법의 결과는 거의 같지만 차이가 있다.
(1) 클래스는 항상 엄격 모드(use strict)로 실행된다.
(2) 클래스 메서드는 열거할 수 없다.
(3) class로 만든 함수엔 특수 내부 프로퍼티인 [[FunctionKind]]:"classConstructor" 가 붙는다.
생성자를 호출할 때에는 반드시 new 연산자를 사용해야 하는 이유이기도 하다. JS 엔진은 함수에[[FunctionKind]]:"classConstructor"가 있을 때 new 와 함께 호출하지 않으면 에러를 발생시킨다.




1-2. 클래스의 표현식

Class 표현식은 class를 정의하는 또 다른 방법이다.
Class 표현식은 이름을 가질 수도 있고, 갖지 않을 수도 있는데, 이름을 가진 class 표현식의 이름은 클래스 body의 local scope에 한해 유효하다. 쉽게 말해 클래스 내부에서만 사용 가능하다는 것이다.

// ex.1) 앞에서 본 예제를 표현식으로 바꾸면 이렇게 된다.
let User = class { 
  constructor(name) {
    this.name = name;
  }
  sayHi() {
    alert(this.name);
  }
};
const my = new User("seul");
my.sayHi();

console.log(User.name);  // User



// ex.2) 
let User = class User2{ 
  constructor(name) {
    this.name = name;
  }
  sayHi() {
    alert(this.name);
  }
};
const my = new User("seul");
my.sayHi();

console.log(User.name);  // User2 
console.log(User2);  // ReferenceError: User2 is not defined

✍️ 하지만, 클래스의 (인스턴스 이름이 아닌) name 속성을 통해서는 찾을 수 있다.




2. getter & setter

❓ getter와 setter는 언제 쓰일까? 예제를 통해서 알아보자. 🧐

class User {
  constructor(firstName, lastName, age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }

  get age() {
    return this._age;
  }

  set age(value) {
       /* if (value < 0){
        *     throw Error('나이는 음수로 설정할 수 없어요!');
        * }   // 이렇게 적을수도 있다. */
    this._age = value < 0 ? 0 : value;
  }
}

const user1 = new User("seul", "lee", -15);
const user2 = new User("eun", "kim", 16);
console.log(user1.age);
console.log(user2.age);

❗️ 아래 user1 에 'seul lee' 의 나이를 '-15'로 '잘못' 입력했다.
✍️ 사용자가 실수로 나이를 -로 설정하면 그대로 -15로 출력될 것이다. 이것은 객체지향적인 개념으로 봤을 때 맞지 않는다고 생각될 수 있다. 이렇게 클래스를 사용하는 사용자, 혹은 내가 만든 클래스를 동료가 잘못(의도치 않게) 사용하더라도, 우리가 먼저 선방어적으로 만들 수 있도록 도와주는 것이 getter와 setter라고 이해했다.

(1) get 이라는 키워드를 이용해서 값을 리턴하고, set 을 이용해서 값을 설정한다. 대신 set 은 값을 설정하므로 값을 받아와야 한다.(value)
+) 여기서 this.age 라고 설정하면 에러가 발생한다.

  • 1) age getter 를 정의하는 순간, 5행의 this.age 는 메모리에 올라가있는 데이터를 읽어오지 않고 바로 getter를 호출한다.
    2) age setter 를 정의하는 순간, 바로 메모리에 값을 할당하는 것이 아니라 setter를 호출하게 된다.
    3) setter안에서 전달된 valuethis.age 에 할당할 때, 메모리의 값을 업데이트 하는 것이 아니고, value; 는 setter(자기자신)를 호출하게 되는 것이 반복된다.
    4) 이것을 방지하기 위해서 getter와 setter안에서 쓰여지는 변수의 이름을 조금 다르게 설정할 필요가 있다. (age2, agePrivate, _age 등

(2) 이렇게 User라는 클래스 안에는 총 3개의 필드가 있다. (firstName, lastName, _age)
(3) 이제 set 을 이용해서 if문 혹은 예제와 같이 작성할 수 있다.

📌 filed_age 이지만, 우리가 .age 라고 호출할 수 있는 것과 _age 에 값을 할당할 수 있는 것은 내부적으로 getter와 setter를 이용하기 때문!




3. Public & Private

생성자를 쓰지 않고 fields 를 정의할 수 있다. 해쉬기호 # 을 붙이면 private class 필드를 선언한다.

class Experiment {
    publicField = 2;
    #privateField = 0;
}
const experiment = new Experiment();
console.log(experiment.publicField);  //2
console.log(experiment.privateField);  //undefined 

private fields 는 클래스 내부에서는 값을 보거나 접근, 변경이 가능하지만 클래스 외부에서는 읽거나 변경이 불가능하다.




4. Static

prototype 가 아닌 클래스 함수 자체에 메서드를 설정할 수도 있는데, 이러한 것을 정적(static) 메서드라고 부른다. static 키워드를 붙여 만들 수 있다.

📌 기본문법 : static methodName() { ... }

(1) 정적 메서드

class User {
	static staticMethod(){
  	console.log(this === User);
  }
}

User.staticMethod();  // true

정적 메서드는 클래스의 인스턴스 없이 호출이 가능하며 클래스가 인스턴스화되면 호출할 수 없다. (종종 어플리케이션의 유틸리티 함수를 만드는데 사용된다.)

특정 클래스 인스턴스가 아닌 클래스 '전체’에 필요한 기능을 만들 때 사용할 수 있다.


(2) 정적 프로퍼티

class Article {
  static publisher = "Hello";
}

alert( Article.publisher );  // Hello

(예제)

class Article {
    
    static publisher = 'Hello!';
    
    constructor(articleNumber) {
        this.articleNumber = articleNumber;
    }

    static printPublisher() {
        console.log(Article.publisher);
    }
}


const article1 = new Article(1); 

console.log(article1.publisher);  // undefined ◀
console.log(Article.publisher);  // 'hello' ◀
Article.printPublisher();  // 'hello'

console.log(article1.articleNumber);  // 1

✍️ console.log(article1.publisher); 에서 undefined 가 뜨는 이유는 static은 오브젝트 마다 할당되어 지는 것이 아니고 Article 이라는 클래스 자체에 주어지기 때문이다.
그래서 console.log(Article.publisher); 처럼 입력하면 값을 받아 올 수 있는 것이다. 😀


프로토타입에 대해 전혀 모르던 때엔 클래스를 이해하기 어려웠는데 프로토타입이 무엇인지 기초적인 부분이라도 알고 공부하니까 도움이 되었다!




reference
web_club javascript_info MDN

profile
기억보단 기록을 ✨
post-custom-banner

0개의 댓글