Front-End/Javascript

javascript - 객체지향언어, class

태나미 2021. 2. 3. 03:00
객체지향언어(OOP)란? 서로 관련 있는 데이터와 명령어들을 묶어서 객체라는 것을 만들고, 이 객체에 지정된 방향대로 실행되는 방식이다.
이렇게 여러가지 객체들을 조립하는 방식으로 프로그래밍하는 것

객체 지향 언어의 특징

캡슐화(Encapsulation)

- 프로그램 내에서 같은 기능을 목적으로 작성된 코드를 모아서 다른 곳(클래스)에서 안 보이게 숨기는 것.

- 클래스에 정의된 속성(Attribute)는 숨기고(Private), 객체가 수행할 기능(Function)은 공개(Public)하는 것.

상속(Inheritance)

- 자식 클래스는 상속받은 부모 클래스의 속성(변수) 및 기능(메서드, 함수)을 물려받는 것.

다형성(Polymorphism)

- 하나의 클래스나 메소드가 다양한 방식으로 동작이 가능한 것을 의미

- 부모클래스가 자식클래스의 값으로 대체되는 overriding

- 메소드의 매개변수(parameter)에 따라 다르게 동작하는 overloading


class Person{
  name;
  age;
  speak();
}

예시와 같이 person이라는 class 안에는

속성( field ) - name, age

행동( method ) - speak

 

class란 fields와 methods를 묶어놓는 것, 간혹 fields만 있는 경우도 있다.

관련 있는 변수와 함수를 묶어놓는 것 = class

class

- template

- 데이터가 들어있지 않고 틀만 만들어 놓는다, 한 번만 선언

- ES 6에서 처음 소개

object

- instance of a class

- 클래스를 이용해서 데이터를 넣어서 만든 것

// class 선언
class Person {
  // constructor
  constructor(name, age) {
    // fields
    this.name = name;
    this.age = age;
  }
  
  //methods
  speak(){
    console.log(`${this.name} hello!`);
  }
}

// object 생성
const taenam = new Person('taenam', 29);
console.log(taenam.name); // taenam
console.log(taenam.age); // 29
taenam.speak() // taenam hello!

Getter and Setters

// class 선언
class User {
  // constructor
  constructor(firstName, lastName, age) {
    // fields
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }
}

// object 생성
const user = new User('jihun', 'seo', -1);
console.log(user.age); // -1

getter 와 setter를 사용하는 이유

객체의 무결성을 보장하기 위함 이다.

 

예를 들어 User이라는 클래스에 age(나이)라는 필드가 존재하지만, 보기와 같이 age에 -1이 할당되었다.

age는 0보다 작을 수 없는데,

 

외부에서 직접적으로 접근할 경우,

age에 -1이라는 값이 들어오면서 객체의 무결성이 깨지는 일이 발생한다.

 

이를 방지하기 위해,

필드를 private로 만들어 외부의 접근을 제한한 후,

Setter를 사용해 전달받은 값을 내부에서 가공해 필드에 넣어주는 방식을 사용하고

 

필드 값을 가져올 때도,

Getter를 사용해 본 필드의 값을 숨긴 채

내부에서 가공된 값을 꺼낼 수 있다.

// class 선언
class User {
  // constructor
  constructor(firstName, lastName, age) {
    // fields
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }
  
  get age () {
    return this.age;
  }
  
  set age (value) {
    this.age = value;
  }
}

// object 생성
const user = new User('jihun', 'seo', -1);
console.log(user.age); // -1

get을 이용해 값을 리턴하고,

set을 이용하여 값을 설정할 수 있다.

set은 값을 설정하기 위해 value라는 파라미터를 받아와야 한다.

 

이렇게 돼버리면 set에서 재귀적으로 호출하기 때문에 콜 스택이 초과된다.

 

age라는 getter 정의하는 순간, getter를 호출한다.

setter 정의하는 순간, 메모리 값을 할당하는 게 아니라, setter를 호출한다.

setter에 전달된 value를 this.age에 할당할 때, setter를 호출한다. (계속 무한으로 자기 자신을 호출한다)

 

그렇기 위해서는 getter와 setter에서 쓰이는 변수의 이름을 다르게 생성한다. '_'은 국룰이다. 

// class 선언
class User {
  // constructor
  constructor(firstName, lastName, age) {
    // fields
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }
  
  get age () {
    return this._age;
  }
  
  set age (value) {
    this._age = value < 0 ? 0 : value;
  }
}

// object 생성
const user = new User('jihun', 'seo', -1);
console.log(user.age); // 0

user라는 obeject에서 this._age 값을 -1로 주었지만,

setter안에서 처리해주기 때문에, user.age는 0으로 찍히는 것을 확인할 수 있다.

Fields( public, private )

- 최근에 추가되어진 것

생성자를 쓰지 않고,

pubilc: class 외부에서 접근이 가능

private: class 내부에서만 값이 보이고, 외부에서 조회, 변경 불가 ( 해쉬 기호(#) 기호 붙인)

class Animal {
  pubilcType = 'panda';
  #privateName = 'pingpong';
}

const animal = new Animal();
console.log(animal.pubilcType); // panda
console.log(animal.privateName); // undefined

Static (properties and methods)

class Article {
  static artist = 'Picasso';
  constructor(num) {
    this.num = num
  }
  
  static printArtist(){
    console.log(Article.artist);
  }
}

const article1 = new Article(1);
const article2 = new Article(2);
console.log(article1.artist); // undefined

console.log(Article.artist); // picasso
Article.printArtist(); // picasso

object마다 할당되는 것이 아니라, class자체에 붙어있다.

object와 상관없이 class에 공통적으로 사용하고 싶을 때 쓰인다.

=> 메모리 사용을 줄일 수 있다.

상속(Inheritance)과 다형성(Polymorphism)

상속, 부모 클래스의 변수/메서드를 자식 클래스가 물려받아 그대로 사용 가능하게 해 준다.

예를 들어, 삼각형, 네모, 원형을 만들고 싶을 때,

각각의 class들을 만들어도 되지만, 공통적인 것을 묶어서 부모 class의 값들을 상속받을 수 있게 만든다.

이렇게 되면, 재사용이 가능해 유지보수도 쉽다.

class Shape {
  constructor(width, height, color) {
    this.width = width;
    this.height = height;
    this.color = color;
  }
  
  draw() {
    console.log(`${this.color}으로 그릴거에요.`);
  }
  
  getArea() {
    return this.width * this.height;
  }
}

// 각각의 class는 Shape class를 상속받았다.
class Rectangle extends Shape {}
class Triangle extends Shape {}

// object 생성
const rectangle = new Rectangle(100, 100, '검정');
rectangle.draw(); // 검정으로 그릴거에요
console.log(rectangle.getArea()); //10000

const triangle = new Triangle(100, 100, '빨강');
triangle.draw(); // 빨강으로 그릴거에요
console.log(rectangle.getArea()); //10000

extends 키워드를 이용해 Shape을 연장한다.

 

triangle의 넓이를 구하면, 5000 값이 아닌 10000이 나오는데, 

Shape class를 상속받으면서, Triangle class를 수정하고 싶다.

이때 필요한 건 다형성이다.

 

다형성, 행동( method )을 여러 방법으로 구현하고, 상황에 따라 적당한 구현을 선택해서 쓸 수 있도록 해주는 기능을 제공해준다.

overriding, 주로 클래스나 인터페이스를 상속받아서 부모 클래스의 변수/메서드를 재정의 해서 사용하는 것

super를 사용하면, 부모의 클래스의 변수/메서드를 재사용할 수 있다.

class Shape {
  constructor(width, height, color) {
    this.width = width;
    this.height = height;
    this.color = color;
  }
  
  draw() {
    console.log(`${this.color}으로 그릴거에요.`);
  }
  
  getArea() {
    return this.width * this.height;
  }
}

// 각각의 class는 Shape class를 상속받았다.
class Rectangle extends Shape {}
class Triangle extends Shape {
  draw() {
    super.draw(); // 빨강으로 그릴거에요
    console.log("🔺");
  }
  getArea() {
    return (this.width * this.height) / 2;
  }
}

// object 생성
const rectangle = new Rectangle(100, 100, '검정');
rectangle.draw(); // 검정으로 그릴거에요
console.log(rectangle.getArea()); //10000

const triangle = new Triangle(100, 100, '빨강');
triangle.draw(); // 빨강으로 그릴거에요
console.log(triangle.getArea()); // 5000

 

 

출처: www.youtube.com/watch?v=_DLhUBWsRtw&list=PLv2d7VI9OotTVOL4QmPfvJWPJvkmv6h-2&index=6