Front-end/JavaScript

[JavaScript ES6]객체지향(OOP) class(생성자,상속,메소드,getter,setter,instanceof)

윤민_ 2022. 1. 10. 22:11

Javascript는 객체지향 프로그래밍 언어이다.

React를 공부하기 시작하며 Javscript 객체지향에 관한 지식이 부족함을 인지하고 공부한 내용을 기록한다.

해당 글은 기존의 글들과 비슷한 내용을 가진 부분들이 있다.

이 글은 최종 정리본에 가까우니 추가적으로 모르는 글들은 

javascript section의 글들을 봐주면 감사하겠다. 😀

✨JS 객체지향 프로그래밍 정리!(Object Oriented Programming, OOP)

! JavaScript OOP의 특징은 ES6이상의 브라우저에서만 동작하는 게 큰 특징이다.

🔹this

var kim = {
    name:'kim',
    first:10,
    second:20,
    sum:function(){
        return this.first+this.second;
        //해당 this는 함수 kim을 가르키는 것이다.
    }
}
//console.log("kim.sum(kim.first, kim.second)", kim.sum(kim.first, kim.second));
console.log("kim.sum(kim.first, kim.second)", kim.sum());

사람들은 객체(kim) 안에 메소드가 존재한다면

자신이 속해있는 객체를 가르키는 특수한 키워드를 만들기로 약속했고 이는 this이다.

쉽게 생각하면 this는 객체 kim을 가리키는 것이다.

 

var kim 이 yoon으로 바뀌거나 lee로 바뀌어도 this는 해당하는 객체를 가리킬 것이다.


🔹class 

class는 객체를 만드는 공장 이라 생각하면 된다.

 

다른 예시로는 class는 붕어빵 틀이라 생각하면 된다.

붕어빵을 만들기 위해서는 내용물(팥, 크림, 피자)을 담아서 만들 틀이 필요하지 않은가?

그 틀 역할을 하는 게 class라 생각하면 된다.

그리고 여기서 사용되는 내용물(붕어빵(피자, 팥, 크림) 종류)은 Object로 보면 된다.

class Person {
  //constructor(생성자)
  constructor(name, age) {
    //fields
    this.name = name;
    this.age = age;
  }

  //methods(행동)
  speak() {
    console.log(`${this.name}: hello!`);
  }
}

const yoon = new Person("yoon", 23);
console.log(yoon.name);
console.log(yoon.age);
yoon.speak();

상단의 코드는 class를 선언한 코드이다.


🔹class constructor(생성자)

상속은 이전글에서 따로 다룬적 있지만, 중요하기 때문에 한번더 작성한다.

추가적으로 알고싶다면 상속,프로퍼티 글을 보고와도 좋다.

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

var yoon = new Person('yoon', 10, 20);
console.log('yoon:', yoon);
//출력 값으로 Person이라는 객체를 생성한다.-> 출력값: yoon: Person{}

var yoon 함수 뒤에 보면 Person이란 함수 앞에 new가 붙어있다.

Person앞에 new를 붙임으로써 객체(constructer)를 return 한다.
이때 객체가 return 되기 전에 Person함수 안에 있는 코드로 인해서

객체의 속성((fields)이 기본적으로 setting이 된다.

new를 사용한 Person은 생성자 constructer가 된다.

 

즉, constructer가 하는 일은 2가지이다.
1. 객체(Object)를 만든다.
2. 그 객체의 초기 상태(fields)를 setting 한다.


🔹class method

class Person {
  constructor(name, first, second) {
    this.name = name;
    this.first = first;
    this.second = second; 
    }
  sum() {
    return "prototype : " + (this.first + this.second);
  }
}

/*
Person.prototype.sum = function () {
  return "prototype : " + (this.first + this.second);
};
바깥에 이 코드를 사용해도 되고 Person class 안에 sum을 사용해도 된다.
class안에서 사용하는게 더 안정성이 높다.
*/

var yoon = new Person("yoon", 10, 20);
yoon.sum = function () {
  return "this:" + (this.first + this.second);
};
//yoon객체의 메소드 내용("prototype : ")을 ("this:")로 변경한다.

console.log("yoon", yoon);
var lee = new Person("lee", 10, 10);
console.log("yoon.sum()", yoon.sum());
console.log("lee.sum()", lee.sum());

 

⚠유의할 점⚠

 

상단의 코드 중

Person.prototype.sum = function () {
  return "prototype : " + (this.first + this.second);
};

이 코드와 class 객체 안에 있는 sum과 출력 값 은 동일하다.

상단의 코드를 사용해도 되고, Person class 안에 sum을 사용해도 출력에는 문제가 없지만.
class안에서 사용하는 메소드 방식이 더 안정성이 높기 때문에 이를 추천한다. 


🔹 Inheritance (상속)과 다양성

class Person {
  constructor(name, first, second) {
    this.name = name;
    this.first = first;
    this.second = second;
  }
  sum() {
    return "prototype : " + (this.first + this.second);
  }
}

class PersonPlus extends Person{
  //extends Person에 의해 Person 안에있는 로직들을 사용 할 수 있다.
  avg() {
    return (this.first + this.second) / 2;
  }
}

var yoon = new PersonPlus("yoon", 10, 20);
yoon.sum = function () {
  return "this:" + (this.first + this.second);
};

상단의 코드 중

class PersonPlus extends Person{
  //extends Person에 의해 Person 안에있는 로직들을 사용 할 수 있다.
  avg() {
    return (this.first + this.second) / 2;
  }
}

Rectangle 객체가 Shape객체 안에 있는 로직(fields)들을 상속받아서 사용할 수 있게 된다.

이렇게 상속을 사용하는 이유는 왜일까?

class Person {
  constructor(name, first, second) {
    this.name = name;
    this.first = first;
    this.second = second;
  }
  sum() {
    return "prototype : " + (this.first + this.second);
  }
}

class PersonPlus {
	constructor(name, first, second) {
    this.name = name;
    this.first = first;
    this.second = second;
  }
  sum() {
    return "prototype : " + (this.first + this.second);
  }
  avg() {
    return (this.first + this.second) / 2;
  }
}

var yoon = new PersonPlus("yoon", 10, 20);
yoon.sum = function () {
  return "this:" + (this.first + this.second);
};

상단의 코드를 보면 알 수 있다.

Person Plus와 Person객체 안에 있는 로직들이 반복되는 것이 보이는가?

상단의 코드처럼 반복적인 구문을 사용하여 코드를 실행하여도 출력 값은 동일하다.

 

그럼에도 상속을 사용하는 이유는 코드의 양을 줄이고

부모 객체 로직의 수정을 통해 자식 객체들의 로직도 한 번에 변경이 가능한

강한 유지보수성을 강점으로 가지고 있기 때문에 사용한다.

코드의 규모가 커지게 된다면 코드 효율성은 매우 떨어진다.


이 코드는 Object 직사각형과 삼각형이

class Shape의 fields(color, width, height)를 상속받아 원하는 값(색과 넓이)을 구하는 코드이다.

내용을 보며 모르는 부분이 있다면 한번 다시 공부를 해보는 것을 추천한다! 

class Shape {
  //fields
  constructor(width, height, color) {
    this.width = width;
    this.height = height;
    this.color = color;
  }
  //methods
  draw() {
    console.log(`drawing ${this.color} color of`);
  }
  //methods
  getArea() {
    return this.width * this.height;
  }
}
//Rectangle에 Shape이 가진 속성을 extends(상속)
class Rectangle extends Shape {}
//필요한 함수들만 수정이 가능하다.
class Triangle extends Shape {
  draw() {
    super.draw();
    //super을 통해 부모 methods를 가져와 사용할수있다.
    console.log("🔺");
  }
  getArea() {
    return (this.width * this.height) / 2;
  }
}

const rectangle = new Rectangle(20, 20, "blue");
rectangle.draw();
console.log(rectangle.getArea());

const triangle = new Triangle(20, 20, "blue");
triangle.draw();
console.log(triangle.getArea());

🔹instanceof

Class checking 즉, 클래스 확인을 위한 기능으로

console.log(("object비교 값")instanceof("class비교 값"))

이러한 형식으로 작성하고

instanceof 왼쪽에 object가 오른쪽에 있는 class의 intance인지 아닌지를 확인하고자 할 때 사용한다.

출력 값은 True, False로 나오게 된다.

console.log(rectangle instanceof Rectangle);
console.log(triangle instanceof Rectangle);
console.log(triangle instanceof Triangle);
console.log(triangle instanceof Shape);
console.log(rectangle instanceof Object); //true javascript의 모든 class는 Object를 상속하기 때문

마지막 구문에서 Class(Object)는 object(rectagle)의 true값 출력된다.

그 이유는 javascript의 모든 class는 Object를 상속하기 때문이다.

object와 같이 javascript에서 설정되어있는 class는 하단의 링크를 통해 볼 수 있다!

 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference

 

JavaScript reference - JavaScript | MDN

This part of the JavaScript section on MDN serves as a repository of facts about the JavaScript language. Read more about this reference.

developer.mozilla.org


🔹Geeter and setter

class user {
  constructor(firstname, lastname, age) {
    this.firstname = firstname;
    this.lastname = lastname;
    this.age = age;
  }
}

const user1 = new user("Steve", "job", -1);
console.log(user1.age);

이 코드를 보면 스티브 잡스의 나이는 -1로 출력된다.

사람의 나이가 -1이 되는 것은 불가능한 일이다.

그렇기에 class에서 초기 설정 (Getter, setter)을 통해 이러한 오류를 해결할 수 있다.

class user {
  constructor(firstname, lastname, age) {
    this.firstname = firstname;
    this.lastname = lastname;
    this.age = age;
  }
  //getter
  //this._age의 값을 메모리에 저장하지 않고 바로 get에 설정
  get age() {
    return this._age;
  }
  //setter
  //=age;의 값을 메모리에 저장하지 않고 바로 set에 설정
  set age(value) {
    //if(value<0){
    //  throw Error('age can not be negative');
    //}
    this._age = value < 0 ? 0 : value;
    //value가 0보다 작으면 0을 출력하고 크면 value를 출력하라는 뜻
  }
}

const user1 = new user("Steve", "job", -1);
console.log(user1.age);

get과 set을 설정하면 return값을 메모리에 저장하지 않고 바로 우리가 설정한 값을 따른다.

+) set의 retrun값은 동일한 age일 때 age의 값을 메모리에 저장하지 않고 set에서 바로 설정하기 때문에

무한 루프가 생긴다. 그렇기에 _age라는 다른 이름으로 설정한 것이다.

 

추가적으로 fields(publick,private)와  static properties methods가 있지만

fields(publick, private)는 최근 나온 구문으로 많은 웹브라우저 지원을 하지 않고

static은 typescript에서 많이 사용하기 때문에 다른 글에서 다루도록 하겠다!

 

 

해당 글은 드림 코딩 엘리님과 생활코딩님의 강의를 기반으로 만든 글이니 틀린 부분 지적은 언제든지 환영합니다!