😵 ~23.11.10

0801 | JS ES6 :: 클래스 (클래스 정의, strick mode, 은닉화 ,static과 인스턴스) / 클래스 상속

unikue 2023. 8. 1. 19:50

✅ 클래스 정의 방법

캡슐화가 완전하지 않다는 단점
클래스라는 형태로 표기법의 변화가 생김 Exam클래스를 '클래스'로 인지하지는 못함

class Exam {
    constructor(kor = 100, math = 100, eng = 100) {
        this.kor = kor;
        this.math = math;
        this.eng = eng;
    }

    total() {
        return this.kor + this.math + this.eng;
    }
}

let exam1 = new Exam(20, 30);
console.log(exam1.total()); // 50
console.log(exam1.kor) // 20
console.log(exam1.eng) // 100
console.log(typeof Exam) // function

- 자바스크립트는 모듈화 불가

- 타입에 대한 형식을 안전하게 사용할 수 있지 X

 

 

 

✅ strict mode

 

Strict mode - JavaScript | MDN

Callout: 가끔 엄격하지 않은 기본값을 " 느슨한 모드 (en-US)(sloppy mode)"라고 부르기도 합니다. 공식적인 용어는 아니지만 혹시 모르니 알아두세요.

developer.mozilla.org

 

- 함수를위한 것

- 클래스는 명시 없이도 strict mode로 처리됨

 


#️⃣ strict mode

JavaScript의 strict mode(엄격 모드)는 ES5부터 도입된 기능으로, 코드를 더 엄격하게 처리하여 일부 오류를 방지하고 안전성을 높이는 모드입니다. strict mode를 사용하면 코드 실행에 대해 몇 가지 제한과 변경 사항이 발생합니다.

strict mode를 활성화하는 방법은 다음과 같습니다:

전역 스코프에서 사용: 스크립트 파일의 맨 위에 "use strict"; 또는 'use strict';를 추가합니다.
함수 스코프에서 사용: 함수의 맨 위에 "use strict";를 추가합니다.
strict mode의 주요 특징과 동작 방식은 다음과 같습니다:

1. 변수 선언:
변수 선언 시 var, let, const를 사용하지 않으면 에러가 발생합니다.
중복된 변수 선언 시 에러가 발생합니다.

2. 전역 객체:
strict mode에서 전역 코드의 this는 undefined가 됩니다. (non-strict mode에서는 전역 객체를 가리킵니다)

3. 예약어 사용 제한:
일부 예약어를 변수 이름으로 사용할 수 없습니다. (eval, arguments, implements, interface, package, private, protected, public, static, yield 등)

4. 함수 매개변수:
함수 내에서 동일한 이름의 매개변수를 사용할 수 없습니다.

5. with 문 사용 금지:
with 문은 사용할 수 없습니다.

6. delete 연산자 사용 제한:
객체의 프로퍼티를 삭제할 때, 해당 프로퍼티가 열거 가능해야만 가능합니다.

7. 함수와 this:
함수를 호출할 때 this가 전역 객체로 바인딩되지 않고 undefined로 설정됩니다. (호출하는 객체가 없을 때)

8. strict mode 비활성화:
strict mode는 함수별로 적용됩니다. 함수 내부에서 strict mode가 아닌 함수를 호출하면, strict mode가 해제됩니다.
strict mode는 프로그램에서 오류를 더 잘 발견하고, 더욱 명시적이고 안전한 코드를 작성할 수 있도록 도와줍니다. 따라서 새로운 코드를 작성할 때는 항상 strict mode를 사용하는 것이 권장됩니다.

 

 


 

 

✅ 은닉화

 

class Exam {
    // 내부에서만 쓸 private한 것은 #으로 멤버에 표현해준다
    #kor
    #math
    #eng

    constructor(kor = 0, math = 0, eng = 0) {
        this.#kor = kor;
        this.#math = math;
        this.#eng = eng;
    }

    total() {
        return this.#kor + this.#math + this.#eng;
    }
}

let exam = new Exam(10, 20, 30);
console.log(exam.kor) // undefined
// console.log(exam.#kor)// 밖에서 접근 불가
console.log(exam.eng) // private처리를 안하면 30이 나오지만 처리하면 undefined
console.log(exam.total()); // 60 public 메서드인 total메서드 자체는 작동된다.

private은 인스턴스 속성으로는 접근 불가

 

 

#️⃣ getter/settter 지원

class Exam {
    // 내부에서만 쓸 private한 것은 #으로 멤버에 표현해준다
    #kor //private
    #math
    #eng

    constructor(kor = 0, math = 0, eng = 0) {
        this.#kor = kor;
        this.#math = math;
        this.#eng = eng;
    }

    total() {
        return this.#kor + this.#math + this.#eng;
    }

    get kor() { //자바에서의 getKor(). kor부분은 밖에 나타나기 위한 함수 명을 기재해주면 됨
        return this.#kor;
    }

    set kor(value) { //자바에서의 setKor(value)
        this.#kor = value;
    }
}

let exam = new Exam(10, 20, 30);
console.log(exam.kor) // 10  접근할때는 exam.getkor이 아니라 exam.kor로 접근하면 알아서 get/set을 구분하여 처리해줌
console.log(exam.eng) // undefined
console.log(exam.total()); // 60

 

 

 

#️⃣ 정적 멤버인 static과 static을 초기화하는 공간

class Exam {
    // 내부에서만 쓸 private한 것은 #으로 멤버에 표현해준다
    #kor
    #math
    #eng
    static count; // static count=10;으로 여기서 해도되지만, 초기화하는 공간을 따로 만들 수도 있다.
    
    static {
        Exam.count = 10; // 초기화할때 count=10이 아닌 Exam.count=10으로 클래스명이 따라다녀야 함.
        //this.count도 가능하지만 static은 인스턴스가 모두 공유하는 변수이므로 바람직하지않음
    }

    constructor(kor = 0, math = 0, eng = 0) {
        this.#kor = kor;
        this.#math = math;
        this.#eng = eng;

        Exam.count++; // 생성자가 생성될떄마다 하나씩 가지는 정적변수. 인스턴스를 생성할 필요가 없으므로 클래스이름과 함께 기재함
    }

    total() {
        return this.#kor + this.#math + this.#eng;
    }

    get kor() {
        return this.#kor;
    }

    set kor(value) {
        this.#kor = value;
    }

    get eng() {
        return this.#eng;
    }
}

let exam1 = new Exam(10, 20, 30);
let exam2 = new Exam(50, 60, 70);
console.log(exam1.kor) // 10
console.log(exam2.eng) // 70
console.log(exam1.total()); // 60
// console.log(count); //class안에 있으므로 strict모드가 되어서 undefined로 자동처리되지않음. count is not defined로 오류발생
console.log(Exam.count); // 12 인스턴스가 두개 생성되면서 두번 더해짐

 

class Exam {
    // 내부에서만 쓸 private한 것은 #으로 멤버에 표현해준다
    #kor
    #math
    #eng
    static count;

    static {
        Exam.count = 100;
    }

    static getKor() {
        return this.kor;
    }
}

//-------------------
static getKor(exam1) { //만약 static에서 인스턴스의 속성에 접근하고 싶다면 해당 인스턴스를 전달해줘야 사용가능
        return exam1.#kor;
    }
    
console.log(Exam.getKor(exam1)) //10

👉 static은 exam.getKor()로 호출해도 exam을 전달하지 않는다. 인스턴스 함수가 자신의 인스턴스를 가리킬때 this를 사용하므로!

 

 

// static 멤버가 private일 때
class Exam {
    #kor
    #math
    #eng
    static #count; //staic 멤버도 private으로 만들기

    static {
        Exam.#count = 100;
    }

    static set count(value) {
        Exam.#count = value;
    }

    static get count() {
        return Exam.#count;
    }

    constructor(kor = 0, math = 0, eng = 0) {
        this.#kor = kor;
        this.#math = math;
        this.#eng = eng;

        Exam.count++; // setter에서 작동함. Exam.#count++로 기재하지 않아도 됨
    }
}

console.log(Exam.count); // getter로 작동하므로 Exam.#count로 접근하는게 아님

👉 static 변수를 private으로 만든 경우, 동일하게 getter, setter를 설정해주면 된다. 단지 접근만 Exam으로 함.

 

 

 

#️⃣ static과 인스턴스 메서드 문제 풀이

더보기
class Triple {
    static triple(n) {
        n = n || 1;
        return n * 3;
    }

    triple(n) {
        n = n || 1;
        return n * 5;
    }
}

class BiggerTriple extends Triple {
    static triple(n) {
        return super.triple(n) * super.triple(n);
    }
}

console.log(Triple.triple()); // 3
console.log(Triple.triple(6)); //18
console.log(BiggerTriple.triple(3)); // 81

var tp = new Triple();
console.log(BiggerTriple.triple(3));  //81
console.log(tp.triple()); //5 -- 인스턴스의 triple메서드이므로 static triple(n)이 작동되는게 아니라 객체의 함수인 triple(n)이 작동된다
console.log(tp.constructor.triple(4)); //12

 

 


 

✅ 클래스 상속

class Exam {
    #kor
    #math
    #eng
    static count= 100;

    constructor(kor = 0, math = 0, eng = 0) {
        this.#kor = kor;
        this.#math = math;
        this.#eng = eng;

        Exam.count++; // 생성자가 생성될떄마다 하나씩 가지는 정적변수. 클래스이름과 함께 기재함
    }

    total() {
        return this.#kor + this.#math + this.#eng;
    }

    get kor() {
        return this.#kor;
    }

    set kor(value) {
        this.#kor = value;
    }
}

console.log("상속 ------------------------------------")

class NewlecExam extends Exam {
    #com;

    constructor(kor, math, eng, com) {
        super(kor, math, eng); // 부모의 생성자를 호출 -- 인자가 없으면 super()이지만 여기선 초기화기키고 싶은 애들을 지정
        this.#com = com; // 자손이 초기화
    }

    total() { // 오버라이드
        return super.total() + this.#com;
    }
}

let exam3 = new NewlecExam(50, 50, 50, 50);
console.log(exam3.total()); // 200

console.log(typeof exam2, typeof Exam, typeof NewlecExam); // true true
console.log(exam1 instanceof Exam, exam3 instanceof Exam) // false true
console.log(exam1 instanceof NewlecExam, exam3 instanceof NewlecExam) // false true
console.log(Object.hasOwn(exam1.__proto__, 'total')) // true  entity에 total property가 존재하는가?
console.log(Object.hasOwn(exam3.__proto__, 'total')) // true  entity에 total property가 존재하는가?
console.log(Object.hasOwn(exam1, 'total')) // false

👉 exam1은 객체. Object.hasOwn은 개체(Exam)에 해당 property가 정의되었는지를 확인하므로, exam,'total'은 false가 반환된다.

 

 

 

지정된 개체가 지정된 속성을 직접 정의한 경우 true. 그렇지 않으면 false