본문 바로가기

[자기계발]/유튜브로 코딩배우기

[드림코딩 엘리] class와 object의 차이점, 객체지향 언어 클래스 정리

반응형

이번 편의 핵심은 사물과 물체들을 class로 정리할 수 있는 눈을 기르는 능력을 갖는 것이다. 강의가 끝나고 어떻게 저것들을 class로 만들 수 있을지 생각해보라. 또한 쇼핑몰 만들 때 or 수강신청 프로그램을 만들 때 어떻게 class로 잘 정리할 수 있을까를 생각해보자.


Class 와 Object

  • class를 이용해서 상속과 다양성이 일어날 수 있는데 이런 모든 것들이 가능한 것이 바로 객체지향 언어이다.
    e.g.) 캡슐화, 상속, 다양성 등
  • person 이라는 class 안에는 name과 age 라는 속성이 있고, speak() 말하는 function이 들어있다.
  • class 안에는 이름과 나이 같은 속성(field)가 들어있고, 말하는 행동(method)가 들어있다.
  • class는 조금 더 연관이 있는 데이터들을 묶어 놓은 fields와 metohds가 종합적으로 묶여있는 것을 말한다.
  • 관련이 있는 변수나 함수들을 묶어 놓은 것들을 class라고 하고, classs 안에서도 내부적으로 보여지는 변수와 밖에서 보일 수 있는 변수들을 나누어어서 이런 것들을 '인큐베이션 캡슐화'라고 한다.
  • data class: class 안에 methods는 들어있지 않고 data만 들어있는 것
  • 프로그래밍 할 때도 사물과 물체들을 class로 / object로 정해서 프로그래밍 하는 것이 조금 더 자연스럽고 유연하다.
  • 객체지향 언어로 프로그래밍을잘하는 개발자 = 풀어야 되는 문제나 구현 해야 되는 기능을 객체로 잘 정의해서 만들 수 있는 개발자 
class person{ 
      name; // field
      age; // field
      speak(); // method
   }

Class

  • template (청사진)
  • declare once
  • no date in
  • 붕어빵을 만들 수 있는 틀
  • class 자체에는 date 가 들엉 있지 않고, 틀만 template만 정해 놓는 것이다.
  • 이런 class 에는 이런 date 만 들어올 수 있어 라고만 정의를 해놓는 것이다.
  • 한 번만 선언한다.

object

  • instance of a class
  • created many times
  • data in
  • 이 class를 이용해서 실제로 data를 넣어서 만드는 것이 바로 object 이다.
  • class를 이용해서 새로운 instancce를 생성하면 object가 되는 것이다.
  • object는 class를 이용해서 굉장히 많이 만들 수 있다.
  • class는 정의만 한 것이여서 실제로 메모리에 올라가지는 않지만 
  • 이렇게 실제로 데이터를 넣으면 이제 object는 메모리에도 올라가게 된다.
  • E.G.) 우리가 붕어빵이라는 class를 이용해서 (팥, 크림, 피자)라는 데이터를 붕어빵에 넣으면 붕어빵 자체는 object이고, 이 붕어빵을 만들기 위해 우리가 정의한 붕어빵의 틀은 class가 된다.

1. class 선언

class는 template에 속하고, 이 template를 이용해서 실제로 data를 넣어서 만드는 것이 object이다.

E.G.) 붕어빵 틀이 class이고 크림, 팥, 피자 같은 data를 넣어서 만드는 것이 object이다.

'use strict';
// Object-oriented programming
// class: template
// object: instance of a class
// JavaScript classes
// - introduced in ES6 // class가 도입되기 전에는 이 class를 정의하지 않고 바로 object를 만들 수 있었다.
// - syntactical sugar over prototype-based inheritance 문법상으로 달달한 각자의 편리함을 제공하는 것을 의미한다.
// 기존에 존재하던 prototype을 기반으로, 그 위에 우리가 좀 간편하게 쓸 수 있도록 문법만 class가 추가된 것이다.

// 1. Classs declarations
class Person { // class라는 키워드를 이용해서 Person이라는 class를 만들고 
    // constructor ↓ 이 생성자를 이용해서 object를 만들 때 필요한 데이터를 전달한다.
    constructor(name, age) {
        //fields ↓ 
        this.name = name;  // 전달받은 데이터를 이 class에 존재하는 2가지 name 과 age 에 전달된 데이터를 할당해주는 것이다.
        this.age = age;
    }

    //methods ↓  
    speak() {
        console.log(`${this.name}: hello!`); // 단순히 this.name을 출력하면서 hello! 이렇게 인사하는 speak이다.
    }
}

// Object 생성  -잘 정리한 class를 이용해서 ellie를 만들어보자.
//새로운 object를 만들 때는 new라는 키워드를 쓴다.

const ellie = new Person('ellie', 20); // 새로운 object 생성 
console.log(ellie.name); // console.log 로 잘 출력되는지 확인이 가능하다.
console.log(ellie.age);
ellie.speak(); // 말하는 methods 인 speak 이렇게 함수 호출이 가능하다
// 결과는 ellie: hello!
// this라는 것은 생성된 object.name 이렇게 하기 때문에 ellie의 이름이 출력되는 것이다.

2. Getter and Setters

  • C 언어는 Procedure (특정한 로직을 처리하기만 하고 결과 값을 반환하지 않는 것 / 함수를 불러서 함수를 호출해서 실행하는 프로그램) 
  • 자바는 Object oriented program 객체 지향 언어 ( object 가 서로 interaction 하면서 돌아가는 application을 만들 때 사용한다)
  • 인캡슐레이션(캡슐화) - 우리가 자주가는 커피 vending machine => class 
    자판기는 커피가 있다. 자판기 커피 갯수가 있다고 하자 => integer(정수) number of coffee 
    커피머신으로 동전을 넣고 커피를 뽑는다. 
    coffee machine 에는 property가 number of coffee / methods는 2개 put coin & make coffee 
  • number of coffee 가 integer인데 -1 이 가능할까? 안된다. 그래서  우리가 getter 와 setter를 쓰는 것이다. 
  • 사용자가 -원이라고 설정하면 안되니까 우리는 setter에서 0으로 만들어 주는 것이다.
  • 다른 사람이 nuber of coffee 를 설정하는 것은 좋지 않다. 그래서 이 number of coffee 라는 property를 private으로 만드는 것이다.
  • 즉 우리가 작성한 class를 옆에 있는 동료가 바보같이 잘못 사용해도 우리가 조금 더 방어적인 자세로 만들 수 있ㄷ도록 해주는 것이 '게터와 세터'이다.
// 2. Getter and setters
class User {
    constructor(firstName, lastName, age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

   // user의 age가 -1 일아는 것은 말이 안되기 때문에, get 키워드를 이용해서 값을 return
   // 사용자가 get age를 호출하게 되면 우리는 this.age를 return 해야한다.

    get age() {
        return this._age; 
    }
    
	// set 키워드를 이용해서 값을 설정할 수 있다. 
    //단, set은 값을 설정해야 해서 value를 받아와야한다. 
    set age(value) { 
        // if (value < 0) {
        // throw Error(`age can not be negative`);
        // }
        this._age = value < 0 ? 0 : value; // 새로운 value를 받으면 우리가 this.age를 value로 설정하게 된다. value 가 -라면 0을 쓰고 아니면 지정된 value를 쓰겠다.
    }
}

const user1 = new User('Steve', 'Job', -1);
console.log(user1.age);

+ 이해를 위한 영상 댓글

class 내에 getter 와 setter 가 specifically define 되어있다면, 그 define 되어있는 property 에 한해서 accessor로 작용합니다. 그래서 위에 예제에서 age 에 대한 getter / setter 가 정확하게 define이 되어있기 때문에, 이제 저 "User" object에서의 age를 access 하려고하면 자동으로 JS가 getter / setter를 call 합니다.

그래서 this.age = age; 에서 'this.age =' 는 set age(value)를 call 합니다. 그러면 이제 여기 set age()을 보시면 set age(value){ this.age = value; } set age가 처음에 호출되고 function body를 execute할때, this.age = value; 여기서 "this.age = " 는 다시 set age(value)를 호출하고, 다시 호출된 set age 에서 "this.age = "는 또 다시 set age(value)를 호출하고.. infinite recursion이 되서 callstack overflow 에러가 발생하죠. 그래서 나온 방법이 private property convention을 이용해서 getter 와 setter 안에 따로 age를 다른 이름으로 사용하자고 해서 _age로 define해줍니다.

set age(value){ this._age = value; } 그러면 이제 다시 constructor 로 돌아가서, this.age = age; 이 실행될때 "this.age = "는 set age()를 호출하고, set age()의 body에서보면 실제로는 age가 아니라 _age 라는 또 다른 이름의 변수에 저장합니다. 여기서 _age는 setter 가 define되어있지 않기 때문에 바로 메모리에 _age의 값을 저장합니다. 실제로는 _age라는 변수가 저장된 것이기 때문에, User object에서 age가 아닌 _age도 직접 access해보면 age랑 같은 값인 것을 볼 수 있습니다. const user1 = new User('Java', 'Script', 10); console.log(user1.age); console.log(user1._age); 하면 둘다 같은 10이 나옵니다.

실제로는 age가 아니라 _age에 저장되었는데 왜 user1.age 도 10이 나오냐? 위에서도 설명했듯이 자동으로 getter를 호출하기때문에 getter에서 우리가 _age 값을 return하기로 정의를 바꾸어 주었기때문에 user1.age 도 10을 리턴하는 것입니다.

3. Public & Private 

너무 최근에 추가된 것이라서 그냥 알고만 있어라.

// 3. Fields (public, private)
// Too soon! 너무 최근에 추가되었다.
// https:// 이거는 엘리노트 복붙
    class Experiment {
        publicField = 2; // public => 외부에서 접근이 가능하다
        #privateField = 0; // #을 붙이면 class 내부에서만 값이 보여지고 접근이 되고 값이 변경이 가능하지만 클래스 외부에서는 이 값을 읽을 수도 변경할 수도 없다.
      }
      const experiment = new Experiment();
      console.log(experiment.publicField); // 결과 2
      console.log(experiment.privateField); // 결과 undefined

4. Static

너무 최근에 추가된 것이라서 그냥 알고만 있어라2 

 

class 안에 있는 fields와 methods 들은 새로운 object를 만들 때 그대로 복제 되어서 값만 우리가 지정된 값으로 변경이 되어서 만들어 지는데, 간혹 object, data와 상관없이 class가 가지고 있는 고유한 값과 이런 data와 상관없이 동일하게 반복적으로 사용되어지는 methods가 있을 수 있다. 그런 것들을 이렇게 static이라는 키워드를 이용해서 붙이면 object에 상관없이 class 자체에 연결되어 있다. 

// 4. Static properties and methods
// Too soon! 이것 또한 너무 최근에 추가되었다.
class Article {
    static publiser = 'Dream Coding';
    constructor(articleNumber) {
        this.articleNumber = articleNumber;
    }

    static printPubliser() {
        console.log(Article.publiser);
    }
}

const article1 = new Article(1); // Article 1과 2를 object를 만들게 되면, static 없이 우리가 object를 이용해서 publisher를 출력할 수 있었을 것이다.
const article2 = new Article(2); // 그런데 결과가 undefined 가 뜬다. 그 말은 이 object 안에 publiser 는 몰라 값이 지정되지 않았어 라고 알 수 있다.
console.log(Article.publiser); // static은 object 마다 할당 되어 지는 것이 아니라 article 이라는 class 자체에 붙어있기 때문에 여기 class를 Article 로 바꾸니까 Dream Coding 이 출력된다. 
Article.printPubliser(); // Static 함수를 호출 할 때도 class 이름을 이용해서 printPubliser 라고 호출하면 출력이 된다.

 

5. 상속 & 다양성

  • 삼각형, 직사각형의 공통점은 넓이를 구할 수 있다는 것이다.
  • 그래서 class shape 활용이 가능하다. 이것은 재사용이 가능하기 때문에 유지 보수하기가 쉽다. 공통적인 shape에 와서 수정하면 된다. 
  • 연장한다는 키워드(extends)만 이용해도 바로 shape에 있는 모든 것들이 Rectangle에 포함이 된다.
  • 상속을 이용하게 되면 공통되어지는 애들을 일일이 작성하지 않아도 extends를 이용해서 동일한 것들을 재사용할 수 있다. 
// 5. Inheritance
// a way for one class to extend another class.
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;
    }
}

class Rectangle extends Shape {}
class Triangle extends Shape {
    draw() { // 여기서 위에 쓴 draw를 overwriting하면 삼각형이 출력되도록 할 수 있다. 
    //하지만 우리가 draw라는 method를 overwriting 했기 때문에 더이상 shape 정의된 draw가 호출되지 않는다. 둘 다 쓰려면 밑에 super.draw를 써야한다. 
        super.draw(); // 부모에 draw라는 함수를 호출하게 되면 이렇게 부모의 methods도 호출되고 그에 이어서 우리가 정리한 draw methods가 호출된다.
        console.log('🔺');
    }

    getArea() {
        return (this.width * this.height) / 2; // 삼격형 넓이 공식을 써야한다. 또 필요한 함수들만 재정리해서 쓰는 것 => overwriting  
    }

    toString() { // 6번을 위해 추가한 것. 좀 더 의미있는 데이터를 만들기위해 쓴 것이다.
        return `Triangle: color: ${this.color}`;
    }
}

const rectangle = new Rectangle(20, 20, 'blue');
rectangle.draw();
console.log(rectangle.getArea());
const triangle = new Triangle(20, 20, 'red');
triangle.draw();
console.log(triangle.getArea());

6. instanceOf

object에 CTRL + CLICK 하면 정의된 부분으로 갈 수 있다. JS의 모든 object는 이 object를 상속한 것이다.

// 6. Class checking: instanceOf operator 
// 왼쪽에 있는 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); // false  => true 이다. triangle은 shape를 상속했다.
console.log(triangle instanceof Object); // false => true 이다. 우리가 JS에서 만든 모든 object classs들은 이 object를 상속한 것이다.
console.log(triangle.toString()); // 우리는 어떤 object 던지 공통적으로 존재하는 methods를 쓸 수 있다.
// 이것의 결과는 [object Object] 인데 5번에 toString() 이것을 추가함으로써 의미있는 데이터로 만들 수 있다. 

let obj = { value: 5 };
function change(value) {
  value.value = 7;
}
change(obj);
console.log(obj);

#자바스크리트 object 

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

 

반응형