TIL

WorldWannyWeb.·2020년 10월 21일
0
post-thumbnail

2020-10-21

Class or Object

class는 constainer와 같은 것이다.

class와 object가 없었다면?
우리가 정의한 변수들이 여기저기 동동 떠다니면서 규모있는 프로젝트 하기 어려울것이다.
class는 조금 더 연관있는 data를 한데 묶어놓는 container라고 생각하면 쉽다.


class person{
name; // properties
age; // properties name과 age는 속성(field)
speak(); // speak을 하는 function  행동(method)
} 

class는 조금 더 연관있는 data들을 묶어놓은 이 field와 method가 종합적으로 묶여있는 것이라고 생각하면 된다.
간혹 class안에는 methods는 없고, data만, fields만 들어있는 경우가 있다. 그렇게 많이 쓰기도 하는데, 그런 경우를 data class라고 부른다. 관련있는 변수나 함수들을 묶어놓은 것을 class라 한다면, class 안에서도 내부적으로 보여지는 변수와, 밖에서 보일 수 있는 변수들을 나누어서 이런 것들은 encapsulation즉, 캡슐화 라고도 한다. class를 이용해서 상속과 다양성이 일어날 수 있다.
이런 모든것들이 다 가능한 곳이 object oriented language 즉, 객체지향언어이다.


1. Class


Class

  • template
  • declare once
  • no data in

청사진, template이라고도 한다.
class 자체에는 data가 들어가있지 않고, 틀, 즉 template만 정해져 있는것이다.
이런 class에는 이런 data만 들어올 수 있다고 정의만 해놓고 한번만 선언한다.
class를 이용해서 실제 data를 만드는 것이 object이다.

object

  • instance of a class
  • created many times
  • data in

그래서 class를 이용해서 새로운 instance를 생성하면 object가 되는 것이다.
object는 class를 이용해서 굉장히 많이 만들 수 있고, class는 정의만 한것이어서, 실제로 메모리에 올라가지는 않는다. 그렇지만 실제로 data를 넣으면 object는 실제로 메모리에도 올라가게 된다. 그래서 class라는 붕어빵을 이용해서 팥을 넣으면 팥붕어빵, 크림을 넣으면 크림붕어빵, 피자를 넣으면 피자붕어빵을 만들 수 있는 것이다. 이렇게 만들어진 붕어빵 자체는 object, 붕어빵을 만드는 틀은 class가 되는 것이다.

Object-oriented programming

class: template
object: instance of a class
자바스크립트에 class가 도입된지 얼마되지 않았다.

JavaScript classes

  • introduced in ES6
  • syntactical sugar over prototype-based inheritance

기존에 존재하던 자바스크립트 위에 추가된 것이기 때문에 완전히 새롭게 추가된 것이 아니다.
기존에 존재하던 prototype을 기반으로 한 것 위에 문법만 class가 추가된 것이다. 문법상으로 달달한, 편리함을 제공한다고 해서 syntactical sugar이라고 한다. 위의 설명들은 언어자체에서 언어구현상의 detail이다.


class가 도입되기 전에는, class를 정의하지 않고, 바로 object를 만들 수 있었다.
이 object를 만들 때, function을 이용해서 template을 만드는 법이 있었다.
class를 이용해서 template을 만들어보자

// 1. Class declarations
class Person{ // class 키워드를 통해 Person을 만들고, 
    //constructor
    constructor (name, age){ 
    // constructor 생성자를 이용해서 나중에 object를 만들때, 필요한 data를 전달
        //fields
        this.name = name;
        // 전달받은 data를 class에 존재하는 두가지 fields, name과 age에 전달된 data를 할당
        this.age = age;
    }
    //methods
    speak(){ // 말하는 speak method도 존재하게 된다. 
        console.log(`${this.name}: hello!`); 
	// class에 있는 this.name을 출력하면서 hello 하는것
        // this는 생선된 object.name 이라고 하기 떄문에 아래의 wanny에서 wanny 의 이름이
	// 출력된다.
    }
}

//이렇게 정리한 class를 이용해서 object를 만들어보자
const wanny = new Person('jungwan', 28);
console.log(wanny.name);
console.log(wanny.age);
wanny.speak();

2. Getter and setters


캡슐화를 알아보자면,

커피머신으로 생각해보자
class = coffee vending machine

자판기에는 커피가 있다.
number of coffee (커피의 갯수) = integer
int cup number of coffee

커피머신에서 하는일
1. 동전을 넣고
2. 커피를 뽑는다.

그러면, coffee machine에는
property : number of coffee, put coins(method), make coffee(method)
이때, number of coffee가 integer인데 -1로 설정되는 것이 맞을까?

당연 틀리다. 최소 개수가 0이어야지, -1은 있을 수 없다.

그래서 getter와 setter를 사용하는 것이다 즉, 사용자가 -1이라고 설정하면 안되기 때문!
사용자가 -1로 설정해도, 우리가 setter에서 0으로 만들어주는것이다.

근데 다른사람이 number of coffee를 설정하는 것이 좋을까?

다른사람이 설정을 하는것은 좋지 못하기 때문에, number of coffee라는 property를
private하게 만드는 것이다. 이게 바로 Encapsulation 즉, 캡슐화이다.

class User {
    constructor(firstName, lastName, age){
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
    // age가 -1이 되는 것은 말이 안되는 일이기 때문에 get키워드를 이용해 value를 return
    get age(){ // 사용자가 get age()를 호출하게 되면 this.age를 return 
        return this._age;
    }

    /*
    우리가 age라는 getter를 정의한 순간(get age()) this.age는 메모리에 올라가있는 데이터를 읽어
    오는 것이 아니라 위에있는 getter를 호출하게 된다. 그리고 우리가 setter(set age(value))를 정
    의 하는 순간 = age;를 호출할때, 즉, 값을 할당할때, 바로 메모리의 값을 할당하는 것이 아니라 
    setter를 호출하게 된다. 
    그말은 우리가 setter 안에서 전달된 value를 this.age에 할당할때 메모리의 값을 업데이트하는 것이
    아니라 setter를 호출하게 된다. setter로 돌아와서 = value는 다시 setter를 부르고를 반복 그래
    서 stack이 가득차는 것. 그것을 방지하기 위해서는 getter 와 setter안에서 쓰여지는 변수이름을 
    _를 붙여서 사용하면 됨 ex) _age 
    그리고 User라는 class 안에는 총 3개의 fields 가 있다. 
    1. firstName 2. lastName 3. _age.  
    */

    // set키워드를 이용해 value를 설정. set은 value를 설정해주기때문에 value를 받아와야한다.
    set age(value){ // 새로운 value를 받으면 this.age를 value로 설정하게된다.
        // if(value < 0){ // _age가 존재함으로 set가 존재함 그래서 -값을 주엇을때 경고할수있다.
        //Throw Error('age can not be negative');
        // } 이방법보다 젠틀하게 하고싶으면 아래 삼항연산자 처럼 적어도 된다.
        this._age = value < 0 ? 0 : value; 
    }
}


const user1 = new User('steve', 'Job', -1);
console.log(user1.age);
// fields는 _기호가 들어간 age도 있지만, .age로 호출할 수 있는 것이나 this.age처럼 
// .age = age로할당할 수 있는 이유는
// 내부적으로 getter와 setter를 이용하기 때문이다.

3. Fields (public, private)


Too soon!
아직 쓰기엔 너무 이르니 알아만 두자!
MDN 참고

// constructor를 쓰지 않고, field를 정의할 수 있는데,
class Experiment {
    publicField = 2; // 그냥 정의하게 되면 public 즉, 외부에서 접근가능
    #privateField = 0; 
    // #기호를 붙이게 되면 private 즉, class 내부에서만 값이 보여지고 접근가능, 변경가능
    // 그렇지만 class 외부에서는 값을 읽거나, 변경불가능 
}

const experiment = new Experiment();
console.log(experiment.publicField); // 2
console.log(experiment.privateField); // undefined

4. Static properties and methods


Too soon!
아직 쓰기에 너무 이르니 알아만 두자!

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

    static printPublisher(){
        console.log(Article.publisher);
    }
}
/*
class 안에 있는 fields 와 method들은 새로운 object를 만들때 마다 고대로 복제되어서
값만 지정된 값으로 변경되어서 만들어진다. 간혹 이런 object. 이런 data와 상관없이
class가 가지고 있는 고유한 값과, data에 상관없이 동일하게 반복적으로 사용되어지는 method
가 있을 수 있는데, 그런것들에 static키워드를 붙이면 object에 상관없이 class 자체에 연결된다.
*/

const article1 = new Article(1);  
const article2 = new Article(2);
//console.log(article1.publisher); // undefined
console.log(Article.publisher);
Article.printPublisher();
/*
article1,2 object를 만들면, static을 사용하지 않았다면
위 console처럼 article1이라는 obj를 통해서 obj에publisher를 출력할 수 있을것이다.
그치만 undefined로 나온다 왜?
object안에 publisher는 '몰라 값이 지정안됬어!'라고하는 것
static은 object마다 할당되는 것이 아니라 class 자체에 붙어있기 때문이다.
article1대신 class Article로 바꾸게 되면 Dream coding이 제대로 출력되는 것을 알 수 있다.
static 함수를 호출할때도 class 이름을 이용해서 printPublisher라고 호출하면 출력되는 것을 볼 수 있다.
타입스크립트에서 굉장히 많이 사용할것이다. object에 상관없이, 들어오는 data에 상관없이 
공통적으로 class에서 사용할 수 있는 것이라면 static과 static method를 이용하는 것이 
메모리의 사용을 줄일 수 있다. 
*/

상속과 다양성


브라우저에 다양한 도형을 그릴 수 있는 app을 만든다고 생각해보자.
직사각형 삼각형 동그라미를 그릴 수 있다고 칠때 이 아이들을 class로 지정한다고 하면 어떻게 지정할 수 있을까?
도형을 나타낼 수 있는 속성을 보면 너비, 높이, 색 등이 있을 것이다. 그리고 기본적으로 drawing이 되고, 색을 칠할 수 있고, 할 수 있는 다양한 method들이 있을 것이다.

여기서 반복되어지는 것은 너비와 높이 이다. 그러나 이것들을 각각 따로 만들어서 동일한 것을 반복하는 것보다는 이 아이들의 큰 공통점은 도형이라는 점을 이용해, shape을 한번에 크게 정의하고 shape에 공통적으로 쓰이는 속성값을 재사용하는 것이 더 간편할 것이다. 재사용이 가능하기 때문에 유지보수도 쉽다.


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 of`);
    }

    getArea(){
        return width * this.height;
    }
}
/*
shape이라는 class를 만든 후 class 안에 총 세가지, width, height, color 세가지의 fields가
있고,draw() 하는 method 하나, getArea() method하나 해서 method가 총 두개가 있다.
*/

class Rectangle extends Shape {}
class Triangle extends Shape {
    draw() {
        super.draw(); 
        console.log(`drawing ${this.color} color of`);
    }

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

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

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

/*
Rectangle class를 만들고 싶다면, 동일하게 게속 반복하지 말고, 
extends 라는 키워드를 사용해서 class Shape을 연장한다. 

이렇게만 정의해도 Shape에서 정의한 fields와 method가 자동적으로 Rectangle에
포함이 된다. 연장한다는 extends만 사용하면 Shape에 있는 모든 것들이 Rectangle에
포함된다. Triangle class를 만들때에도, 상속을 사용하게 되면, 공통되어지는 아이들을 
하나하나 만들지 않아도 extends를 이용해서 동일한것들을 재사용할 수 있다. 

자 그러면 다양성은 무슨일을 할까?
아주 획기적인 일을 하는데, getArea()를 호출하게 되면 rectangle.getArea는 400 이 나온다.
20과 20을 곱하니까! 하지만 triangle.getArea도 400 이 나온다. triangle은 (너비*높이) / 2
이기 때문에 답이 틀린것이다. 여기서 다양성이 빛을 발휘하는데, 필요한 함수만 바로 재정의해서
사용할 수 있다. class Triangle extends Shape{} 안에 getArea를 가져다가 다시 정의해서
사용할 수 있다. 위에처럼! 이것을 overriding 이라고 한다. draw 매소드를 overriding 했기 때문에
더이상 Shape에 정의된 draw가 호출되지 않는 것을 볼 수 있다. 원래 draw도 그려주면서 색다르게
그려주고 싶다 할때는 super을 이용해서 부모에 draw를 호출하게되면, 부모의 method도 호출이 되고
뒤이어 우리가 정의한 draw도 같이 나오게 된다.   
*/

// 6. Class checking: instanceOf
console.log(rectangle instanceof Rectangle); // t
console.log(triangle instanceof Rectangle); // f
console.log(triangle instanceof Triangle); // t
console.log(triangle instanceof Shape); // t
console.log(rectangle instanceof Object); // t

/*
왼쪽에 있는 object는 class를 이용해 만든 새로운 instance이다. instanceOf는
왼쪽에 있는 object가 오른쪽에 있는 class의 instance인지 아닌지 즉, object가 
오른쪽에 있는 class를 이용해서 만들어진 것인지 확인하는 것. 
*/
profile
와니완의 월드와이드와니웹🐥

0개의 댓글