프로젝트/[개인] 프론트엔드 로드맵

[용어정리] Part4 JavaScript | 문법 및 기본구조(3)

MI개발자 2022. 1. 19. 18:13

15 클래스(class)

>es6에 새로 추가된 문법

>JavaScript는 객체지향을 지원하기 위해서 프로토타입(prototype)기반의 방식을 지원하는데, 새로 추가된 class라는 키워드를 통해 좀 더 강력한 객체지향 프로그래밍을 지원하게되었다.

 

mdn에 소개된 class를 보면 class라는 키워드는 새로운 객체지향 상속모델을 제공하는 것은 아니다.

기존의 프로토타입(prototype)방식을 좀 더 명료하게 사용할 수 있도록 돕는 일종의 도우미 라고 생각하는 것이 좋다.

 

  1. 클래스를 만드는 방식에는 2가지가 있다.
    1. 선언적 방식
    2. class  표현식을 변수에 할당


  • 생성자(constructor)
    함수를 통해 객체를 만들때도 함수에 인자로 넣어서, 외부에서 객체를 생성할때 안으로 넣어줄 수 있는 기능이 있는데, class에서도 마찬가지로 이런 기능이있다. 바로 생성자이다. 
    이것을 이용해서 최초의 초기값을 객체 안으로 넣어줄 수 있는 기능을 제공한다.
    즉, A는 생성됐고, B는 생성될때 constructor가 한 번 불렸다는 것을 log찍은 것을 통해 알 수 있다.

    따라서 우리가 보통 생성자를 사용해서 무언가를 인자로 넣어서 객체안에서 활용할 수 있도록 설정을 하게되는데
    이처럼 동작이 되는 것을 알 수 있으며, 인자값을 안넣을 경우 자료형에서 배웠던 것 처럼 undefined 라고 뜨는것을 확인 할 수 있다.

 

 

  • 멤버변수 
    보통 function으로 객체를 만들때도 그 안에서 this.변수 이름을만들고, 거기에 값을 넣어서 설정 하게된다.
    이런것을 class에서는 좀 더 명확하고, 쉽게 할 수 있다.

    //멤버변수 --constructor에서 객체 property를 할당해주는 방법
    class A {
        constructor(name, age) {
            this.name = name;
            this.age = age;
        }
    }
    console.log(new A("Mark", 37));
    
    //class field는 런타임 확인 --위보다 더 쉽게 제공해주는 방식
    class B {
        name; //===this.name
        age; //===this.age
    }
    
    console.log(new B());
    
    class C {
        name = "no name"; //초기값 설정
        age = 0;
    
        constructor(name, age) {
            this.name = name;
            this.age = age;
        }
    }
    console.log(new C("Mark", 37));
    console.log(new C());​


    위와 같은 문법을 사용할때는 node12버전 or 크롬 최신버전에서만 가능한데, 만약 그 이하버전이라면 SyntaxError: Unexpected token; 이라는 Error 메시지를 출력한다.
    즉, 문법적인 오류가 나는 이유는 지금 이 문법(클래스에 필드를 직접 써주는 방식)을 이해하지 못하는 Runtime이라서 그렇다.
    따라서 이렇게 직접 써주는 방식은 자기자신의 Runtime환경이 지원이 되는지 안되는지 확인한다.

    nvm을 사용해서 Runtime을 바꾸기 명령어 :
    $ nvm use 12.11.1

    또한, 보통 상위문법을 이해할 수 있는 문법으로 바꿔주는 역할을 하는 중간단계인 바벨을 많이 사용하게 될텐데 일단은 위와같은 문법이 안될 경우에는 이 문법을 지원하는 상황인지를 체크해보는게 좋다.


  • 멤버함수
    맨아래  class B처럼 함수를 만들고, 함수에서 객체가 가지고있는 자원들을 활용하여 로직을 만들 어 낼 수 있다.


  • get, set(게터, 세터)
    클래스 안에서 get, set이라는 키워드를 이용해서 getter , setter을 만들어보자
    이런일이 왜 생길까?
    class A에서 _name (문법적으로 강제는 아니지만, 언더바 _를 달게되는 경우는 내부적으로 쓸 경우는 _(언더바)를 달고, 멤버변수로 등재하고 get , set은 외부에서 접근할 수 있는  public한 접근 제어자가 된다 그래서 보통은 23번째 줄 처럼 a._name와 같은 직접적으로 바뀌는 행위는 하지않고 get, set을 통해서만 멤버변수를 바꾸는 형식으로 로직을 만든다.

    그래서 readonly를 표현 할 수 있게 되는데,
    set에서의 역할을 함수는 작성하지않고, getter만 두게된다.
     콘솔로 찍은 결과 처음에도 no name 이였는데, 나중에 set이 된 후에도 no name인것을 확인할 수 있다.
    이는 set함수가 없기때문에 마치 readonly처럼 동작하게되는 것이다.

    또한 다시 언급하자면 클래스 필드에있는 _name과 같은 언더바가 있을경우는 "외부에서 값을 바꾸지 말자!" 라는 약속이 되어있는 경우에 의미가 생기는 것이다. 

    + 다른 예제로 더 알아보자!

아래와 getter함수를 설정해보겠다.

※함수앞에 get라고 붙여줘야하며, getter함수에서는 어떤 값을 무조건 반환해줘야한다.

const numbers = {
    a: 1,
    b: 2,
    get sum() {
        console.log("sum함수가 실행됩니다!");
        return this.a + this.b; //여기서의 this는 자기자신, 즉 sumbers를 가르키게되며 
        //numbers가 가지고있는 a와 b를 더해서 반환해주겠다라는것이다.
    },
};

console.log(numbers.sum); //

자, 이렇게 하고나서 numbers.sum을 조회할 때마다 어떤일이 일어나는지 살펴보자!

 

여기서 4번째 줄을 보면 분명 get sum( ) 라는 함수임에도, 

이것을 사용하기위해서 10번째 줄에

console.log(numbers.sum( )) 라고 한게 아니라

console.log(numbers.sum) 라고 ( ) 없이 그냥 조회만 했을 뿐인데 함수가 실행되었다.

 

console.log(numbers.sum);
//sum함수가 실행됩니다!
//3
numbers.b = 5;
console.log(numbers.sum);
// sum함수가 실행됩니다!
// 6

그 아래로 이와같이 b값을 바꿔보고, 실행을 해봤더니 

set sub이 한 번 더 호출돼서, 합이 나타나게된다.

 

이런식으로 getter함수는,
우리가 특정값을 조회하려고할때 ( )로 호출한게아니라,

조회하려고 할 때특정 코드를 실행 시키고, 연산된 값을 받아서 사용하는 것을 의미한다.

 

이제,  setter함수를 설정해보겠다.

※함수앞에 set라고 붙여줘야하며, setter함수에서는 파라미터를 어떤 값을 무조건 설정해줘야한다.

const dog = {
    _name: "멍멍이",
    //_name이라고한 이유는 앞에 무조건 _(언더바) 일 필요없고, 
    //myName이런식으로도 가능하지만 뒤에 setter함수를 만들때 겹치지 않게
    //하기위해서 _를 사용했다.

    set name(value) {
        console.log(this._name + "이름이 바뀝니다.." + value);
        this._name = value;
    },
};

console.log(dog._name); //멍멍이
dog.name = "뭉뭉이";
console.log(dog._name); 
//멍멍이이름이 바뀝니다..뭉뭉이
//뭉뭉이

 

그런데 이제 15번째 줄에서 console.log(dog._name);  보면 dog._name을 해줬는데,

2번째 줄에있는 _name 키값이랑 7번째 줄에있는 set name( )함수랑 이름이 똑같을 수는 없는데,

똑같은 이름의 getter 함수와 setter함수를 넣어줄 수는 있다.

따라서 

const dog = {
    _name: "멍멍이",
    //_name이라고한 이유는 앞에 무조건 _(언더바) 일 필요없고,
    //myName이런식으로도 가능하지만 뒤에 setter함수를 만들때 겹치지 않게
    //하기위해서 _를 사용했다.
    get name() {
        //조회
        console.log("_name을 조회합니다..");
        return this._name;
    },
    set name(value) {
        console.log(this._name + "이름이 바뀝니다.." + value);
        this._name = value;
    },
};

console.log(dog.name); //set name( )이 있으므로 굳이 특정값을 조회하려고할때 _바를 안넣어줘도된다.
//_name을 조회합니다..
//멍멍이
dog.name = "뭉뭉이";
//멍멍이이름이 바뀝니다..뭉뭉이
console.log(dog.name);
//_name을 조회합니다..
//뭉뭉이

이와같이 6번째 줄처럼 똑같은 이름의 get name( )작성하면 되므로,
굳이 특정 값을 조회할때 17번째 주석에서 써있는 것 처럼 _(언더바)를 안넣어줘도된다.

 

마지막으로 예시 하나를 더 들여다보자.  

//---------------------getter  setter
const numbers = {
    _a: 1,
    _b: 2,
    sum: 3,
    calculate() {
        //setter함수에서 호출할 함수이다.
        console.log("calculate");
        this.sum = this._a + this._b;
    },

    //2개의 getter함수는 앞으로 numbers._a혹은 numbers._b를 조회하게된다면, _a혹은 _b를 조회할 수 있게해준다.
    get a() {
        return this._a;
    },
    get b() {
        return this._b;
    },
    set a(value) {
        this._a = value; //value를 받아오면, this._a값으로 넣어준다.
        this.calculate(); //다음 sum값도 update해준다. 그래서 calculate()를 해출해준다.
    },
    set b(value) {
        this._b = value;
        this.calculate();
    },
};

console.log(numbers.sum); //3
numbers.a = 5;
console.log(numbers.sum); //7
console.log(numbers.sum); //7

여기서 주의깊게 봐야할점은 set을 해준후 , calculate()가 호출되는 부분이다.

왜냐하면 조회할 때마다 계산하는 것이 아니라 값이 바뀔때 마다 계산하기때문에 아까 사용한 값을 재사용 하게된다.

 

코드로 더 쉽게 풀어보자면,

만약 setter함수와 getter함수를 만들지 않고, 하나의 getter함수만 사용했다면 

위와 동작이 다르게 진행되는데, 

결과 값을 보면, get sum( )이라는 함수가 조회 할 때 마다 sum ,sum, sum..이 호출되는것을 확인 할 수 있다.

 

따라서 정리해보자면, 아까의 코드와 지금의 코드의 차이점은

아까의 코드는 set함수 즉 값이 바뀔때만 합을 구했는데, 지금의 코드는 조회 할 때마다 합을 구하고있다.

(굳이 다시 더할 필요없이 아까했던거 다시 조회해서 쓰면될 것 같은데)

그래서 지금의 코드가 되게 비효율적이라고 생각된다.

 

getter : 특정값을 조회할때마다 어떤 함수를 호출하는 것이고,

setter : 특정값을 설정할때마다 value를 파라미터로 받아와서 어떤값을 설정할 수도있고, 추가적으로 어떤 코드를 실행할 수도있다. 
setter 예) 
    set b(value) {
        this._b = value;
        this.calculate();
    },

 

 

 

  • static 변수, 함수 (객체가 아니고, 클래스의 변수와 함수)
    >class를 new클래스 명으로해서 객체로 생성한다음 사용하는 변수나 함수가 아니고, 직접적으로 class를 통해서 변수와 함수를 사용하는 방식이다.

    여기서 특이한점은, 
    static name이라고하고, static  name이라고 하는 변수에 이 클래스의 이름은 C가 아니라 라고 적어주면,

    위의 class A에서는 그 클래스는 무엇이냐? 라고했을때 그 클래스 이름이 나왔는데, 
    class C의 결과 값을 보게되면, static의 name 변수에 할당한 값이 C자리에 대신 들어와있는것을 확인할 수있다.
    예)
    A클래스 : [class A]
    C클래스 : [class 이 클래스의 이름은 C가 아니다.] //이를보아 A처럼 C가 들어와야 될것같은데, 할당된 값이 나온것을 보면, static name이라는 변수가 class의 이름을 뜻한다는 것을 알 수 있게되었다.

  • 상속(extends)
    class상속의 기본 - 이미 만들어져있는 class를 활용하는 방법 중에 하나인 상속.

  • 오버라이드(override) - 클래스의 상속 멤버 변수 및 함수 오버라이딩, 추가
    >상속을 활용할때 중요한 요소중에 하나이다.
    >부모에서 구현된 함수나 변수가 자식에게서 똑같이 같은이름으로 구현을 시키면 그것을 override 된다고한다.
    즉, 자식이 만들어 놓은 함수가 부모의 함수를 덮어씌우는 결과를 가지고 오게 된다.
  • super -클래스의 상속 생성자 함수 변경
    >super라는 키워드를 활용하여 자식이 생성자(constructor)에서 뭔가 추가하고자할때 supper을 꼭 호출해야된다는 사실을확인해보자

  • static (클래스의 상속 sstatic상속)
    >클래스 안에있는 static 변수를 상속받기 
    static변수 정상적으로 상속이된다.

 

패스트캠퍼스 강의 자료 

 

보통 바벨을 사용하거나  클래스를 사용할 수 있는 Runtime환경에서 작업을하게되기때문에 클래스에대해 정확히 학습한 후 활용할 수 있도록 공부해놓기! 

 

https://github.com/luckyjek/TIL_/blob/main/basic-js/conditional-statements/class.js

 

GitHub - luckyjek/TIL_: Today I learned / 오늘의 학습 기록소

:bulb: Today I learned / 오늘의 학습 기록소. Contribute to luckyjek/TIL_ development by creating an account on GitHub.

github.com

 

 


참고자료 : 
패스트캠퍼스