[TIL] 객체지향언어(class & object)

정세비·2021년 5월 20일
0

JavaScript

목록 보기
8/16
post-thumbnail

class 안에는 연관있는 데이터들을 묶어놓은 field와 method가 종합적으로 묶여있는 것을 말함.

class human {
  name;
  age; // 이름과 나이같은 속성(field)
  speak(); // speak과 같은 행동(method)
  • 간혹 field만 있는 경우도 있는데 이런 경우 data class라고 부른다.
  • class 안에서도 내부적으로 보여지는 변수와 밖에서 볼 수 있는 변수들을 나눠서 캡슐화라고 부른다.
  • 클래스를 이용해서 상속과 다양성이 일어날 수 있는데 이 모든 것이 가능한 것이 객체지향언어이다.

1. class

  • 클래스는 청사진 또는 template이라고 불림
  • 클래스 자체에는 데이터가 들어있지 않고, 틀 즉 template만 정의를 해놓고 한번만 선언한다.
  • JS에선 ES6에서 class가 도입되었다

2. object

class를 이용해서 실제로 data를 넣어서 만드는 것을 object라고 한다
class를 이용해서 새로운 instance를 생성하면 object가 된다
class는 정의만 한 것이라서 실제로 메모리에 올라가진 않지만, object는 메모리에도 올라가게 된다


(1) Class 선언

class human {
  // constructor
  constructor(name, age) {
  // fields
    this.name = name;
    this.age = age;
  }
  // methods
  speak() {
    console.log(`${this.name}: hi!`);
  }
}

(2) object 생성

const serena = new human('serena', 18); //새로운 object를 만들땐 new를 씀

console.log('serena'.name); // serena
console.log('serena'.age); // 18
serena.speak(); // serena: hi!

(3) getter & setter

getter의 장점

  • getter는 property를 가져올 때 데이터에 대한 작업을 수행한다
  • getter는 조건을 사용하여 다른 값을 반환할 수 있다
  • getter에서 this를 사용하여 calling object의 property에 접근할 수 있다
  • 다른 개발자가 이해하기 쉽다
class user {
  constructor(firstname, lastname, age) {
    this.firstname = firstname;
    this.lastname = lastname;
    this.age = age;
  }
}
  
const user1 = new user('Tom', 'brown', -1);
  • 위의 예에서 나이가 -1이 될 수 없으므로 이를 방어적으로 만들어주는 것이 getter 와 setter임
  • 따라서 get을 이용해서 값을 return하고 set을 통해서 값을 설정할 수 있다
  • 대신 set은 값을 설정하기 때문에 value를 받아와야 함
  • 따라서 사용자가 get을 호출하게 되면 우리는 this.age를 return해야하고 새로운 value를 받으면 this.age를 value로 설정함
class user {
  constructor(firstname, lastname, age) {
    this.firstname = firstname;
    this.lastname = lastname;
    this.age = age;
  }
  
  get age() {
    return this.age;
  }
  set age(value) {
    this.age = value;
  }
}

const user1 = new user('Tom', 'brown', -1);

그런데 위와 같이 하면 계속 call stack size exceeded 에러가 발생한다.
age라는 getter를 정의하는 순간, this.age는 메모리의 데이터를 읽어오는 것이 아니라, getter를 호출하게 되며, setter를 정의하는 순간, = age 는 setter를 호출하게 된다.
즉, setter 안에서 전달된 value를 this.age에 할당할 때 메모리의 값을 업데이트하는 것이 아니라 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) {
    this._age = value < 0 ? 0 : value;
  }
}

const user1 = new user('Tom', 'brown', -1);

따라서 user class 내에는 firstname, lastname, _age의 3개의 field가 존재하게 됨

👉 field에는 _age지만, .age로 호출하고 값을 할당할 수 있는 것은, 내부에 getter와 setter를 이용하기 때문임



(4) public & private

constructor를 쓰지 않고 field를 정의할 수 있다
그냥 정의하게 되면 public field, #을 붙이면 private field

class ex {
  publicfield = 3;
  #privatefield = 5;
}

console.log(ex.publicfield); // 3
console.log(ex.privatefield); // undefined


(5) static

class 내에 있는 field와 method들은 새로운 object를 만들 때마다 그대로 복제되어 값만 지정된 값으로 변경되어 생성된다.

하지만 간혹 이런 data에 상관없이 class가 가지고 있는 고유한 값과 method가 있을 수 있는데 그런 것들은 static이라는 키워드를 붙여 사용하면 object에 상관없이 class 자체에 연결될 수 있다.

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

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

const article1 = new Article(1);
const article2 = new Article(2);

console.log(Article.publisher);
Article.printPublisher();

먄약 static를 사용하지 않았다면
console.log(article1.publisher); 로 publisher를 출력할 수 있었을테지만,

static을 사용함으로써 위는 undefined로 나타난다.

왜냐하면 static은 object마다 할당되는 것이 아니라 article이라는 class 자체에 연결되어있기 때문에 class인 console.log(Article.publisher);로 바꿔주면 'hello'가 출력됨

이처럼 static 함수를 호출할 때도 class를 이용해서
Article.printPublisher(); 로 호출하면 'hello'가 출력됨



(6) 상속 & 다양성

class Shape {
  constructor(width, height, color) {
    this.width = width;
    this.height = height;
    this.color = color;
  }

  draw() {
    console.log(`drawing ${this.color} color!`);
  }

  getArea() {
    return this.width * this.height;
  }
}

위와 같이 3가지 field와 2가지 method가 존재할 때,

Rectangle이라는 클래스를 생성하고 싶을 때, extends라는 키워드를 이용해서 shape을 바로 연장할 수 있다.

class Rectangle extends Shape {}

이렇게만 정의를 내려도, 아래와 같이 자동으로 class shape에서 정의한 field와 method가 Rectangle에 포함이 된다

const rectangle = new Rectangle(20, 20, 'blue');
rectangle.draw(); // output: drawing blue color!
console.log(rectangle.getArea()); // 400

✔ 만약 Triangle의 getArea를 호출할 땐 '다양성'을 활용할 수 있다.
필요한 함수return (this.width * this.height)만 바로 재정의해서 사용할 수 있으며 이를 '오버라이딩'이라고 한다.

const triangle = new Triangle(20, 20, 'red');
class Triangle extends Shape {
  getArea() {
    return (this.width * this.height) / 2;
  }
}

console.log(triangle.getArea()); // 200;

그 외에도 Triangle에 draw를 새롭게 오버라이딩하고 싶다면

class Triangle extends Shape {
  draw() {
    console.log('🔺');
  }
}  

와 같이 작성하면 되는데, 이럴 경우 더이상 shape 내의 draw()가 호출되지 않게 된다. 만약 공통적으로 정의한 draw()도 함께 호출하고 싶다면
super.draw() 를 사용하면 된다.

class Triangle extends Shape {
  super.draw();
  draw() {
    console.log('🔺');
  }
}

triangle.draw(); // drawing red color! 🔺 


(7) instanceOf

왼쪽의 object가 오른쪽 class를 이용해서 만들어진 것인지 아닌지 확인하는 것

console.log(rectangle instanceof Rectangle); // True
console.log(triangle instanceof Rectangle); // False
console.log(triangle instanceof Triangle); // True
console.log(triangle instanceof Shape); // True
console.log(triangle instanceof Object); // True

🔖 object 참고
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference

profile
파주

0개의 댓글