💙 들어가며
ES6에서 ES5의 문제점을 보완한 것들에 대해서 배워보자!
✏️ 학습내용 정리
#ES6에서 크게 달라진 것12: class
기존에 ES5일 때도 객체지향스럽게 구현하려고 노력하였으나
class라는 개념이 없어서 데이터의 은닉성이 보장되지 못했다.
class라는 개념이 생기면서 (function을 통해 확장하는 것이 아니고)
별도의 생성자라는 개념이 생겼다!
(Exam이라는 이름의 함수를 통해서가 아니라 class로 정의할 수 있게 되었고
constructor라는 것이 별도로 생겼다.)
생성자를 통해 기본값을 설정할 수 있다.
그러나 ES5를 그대로 안은채로 ES6가 나온 것이기 때문에
완전하게 달라진 것이라고는 볼 수 없다.
class라는 것을 뜯어보면 사실 틀이 달라졌을 뿐 function이라는 것을 알 수 있다.
(typeof Exam하면 Exam이 안나오고 fucntion이 나옴)
💡 Exam의 타입은 무엇일까?
class Exam {
constructor(kor = 0, eng = 0, math = 0) {
this.kor = kor;
this.eng = eng;
this.math = math;
}
total() {
return this.kor + this.eng + this.math;
}
}
console.log(typeof Exam);
⭕ 출력결과:
Exam의 타입이 Exam 클래스가 아니고 function이라고 인식된다.
class는 보이는 틀만 달라졌을 뿐 사실 함수인 것이다.
(ES5를 그대로 안고 ES6가 등장한 것에 대한 예)

그래도 class의 등장으로 생성자함수가 별도로 생기고
class안에서 편하게 메소드를 정의할 수 있게 되었다.
코드로 한 번 따라 쳐보자!
💡 ES6에서 등장한 class!
class Exam {
constructor(kor = 0, eng = 0, math = 0) {
this.kor = kor;
this.eng = eng;
this.math = math;
}
total() {
return this.kor + this.eng + this.math;
}
}
let exam = new Exam(10, 20, 30);
console.log(exam.total());
⭕ 출력결과:
new해서 생성할 때 넘겨준 매개변수대로 값이 잘 전달되었고
메소드도 잘 호출되었다.

#ES6에서 크게 달라진 것13: Strict mode
기존에 non-strict mode(혹은 비공식적 언어로 sloppy mode)인 경우에는
자바스크립트에서 이미 사용중인 예약어가 변수명으로 쓰여도 오류가 나지 않거나
스펠링이 틀린 변수가 사용되어도 오류가 나지 않고 조용히 처리가 되어서 문제가 있었다.
그런데 이것을 strict mode 사용이라는 키워드로 해결할 수 있게 되었다.
💡 기억할 점:
클래스를 정의하는 순간 별도의 strict mode라는 키워드가 없어도
그 클래스 안에 있는 것들은 다 strict mode로 적용된다.
strict mode는 함수에서 사용될 때 훨씬 더 효과적이다.
strict mode를 전역에서 사용할 수도 있기는 하지만,
그 경우 변수의 스펠링이 틀려도 window의 객체로 인식될 수 있는 경우의 수가 있다.
하지만, 함수에서 사용되면 이런 경우의 수를 차단할 수가 있기 때문이다.
사실 다른 언어들로 따지면 strict mode가 당연한 것이지만
javascript가 워낙 유연한 언어이기 때문에 이것을 strict mode라고 부른다고 한다..
(때문에 다른 언어를 하다가 넘어오면 strict가 잘 체감되지 않을 수도 있겠다.)
💡 함수 안에서 use strict 사용하기
function test() {
"use strict";
let mistypeVariable;
mistypeVarible = 17; //스펠링 오타
console.log(mistypeVarible);
}
test();
⭕ 출력결과:
다음과 같이 스펠링이 다른 것에 대해서 오류를 발생시킨다.

🚨 그러나 전역에서 선언하면 먹히지 않는다.
스펠링이 틀린 변수마저 window 전역 객체로 취급하기 때문!
#private 멤버 선언
캡슐이 깨지지 않기위해서는 캡슐 바깥에서 원하지 않는 데이터의 수정이 되지 않게
캡슐의 속성(멤버변수)를 불러오지 못하도록 해야하는데,
기존에 function으로 할 때는 이런 데이터의 은닉성이 보장되지 않았다.
(그냥 exam.kor로 불러올 수가 있었다.)
그러나 class가 등장하면서 멤버변수를 드디어 private처리할 수 있게 되었다.
💡 멤버변수를 외부에서 사용하지 못하게 하자.
class Exam {
#kor; //#을 붙이면 private이 된다.
#eng; //#을 붙이면 private이 된다.
#math; //#을 붙이면 private이 된다.
constructor(kor = 0, eng = 0, math = 0) {
this.#kor = kor; //때문에 this로 부르는 애들에 모두 #을 붙인다.
this.#eng = eng;
this.#math = math;
}
total() {
return this.#kor + this.#eng + this.#math;
}
}
let exam = new Exam(10, 20, 30);
console.log(exam.kor);
console.log(exam.total());
⭕ 출력결과:
더 이상 외부에서 exam.kor을 불러올 수 없다.
클래스 안의 멤버변수는 안에서만 활용된다.

#getters/setters 등장
클래스에 private을 설정할 수 있으니
원하는 데이터에 대한 접근은 getters와 setters로 처리할 수 있게 되었다.
선언방법은 간단하다. getKor() setKor()와 같이 하면 된다.
💡 getters/setters
class Exam {
#kor;
#eng;
#math;
constructor(kor = 0, eng = 0, math = 0) {
this.#kor = kor;
this.#eng = eng;
this.#math = math;
}
total() {
return this.#kor + this.#eng + this.#math;
}
getKor() {
return this.#kor;
}
setKor(value) {
this.#kor = value;
}
}
let exam = new Exam(10, 20, 30);
exam.setKor(400);
exam.setKor(eam.getKor()+1); //불러온 값에 ++하기
console.log(exam.getKor());
console.log(exam.total());
⭕ 출력결과:
원하는 대로 set으로 데이터를 수정할 수도 있고
get으로 원하는 데이터를 불러올 수도 있다.
total메소드를 실행할 때도 set으로 수정한 속성이 정상적으로 먹혔다.

그런데 자바스크립트에서는 이렇게 사용할 수도 있다.
마치 get과 set을 속성처럼 활용하는 것!
💡 exam.kor에 ++을 하고 싶다!
class Exam {
#kor;
#eng;
#math;
constructor(kor = 0, eng = 0, math = 0) {
this.#kor = kor;
this.#eng = eng;
this.#math = math;
}
total() {
return this.#kor + this.#eng + this.#math;
}
get kor() { //함수인데 속성처럼 사용할 수 있게 해줌
return this.#kor;
}
set kor(value) {
this.#kor = value;
}
}
let exam = new Exam(10, 20, 30);
exam.kor++; //외부에서 kor을 부르면 자동으로 get이 호출된다.
console.log(exam.kor);
console.log(exam.total());
⭕ 출력결과:
getKor과 setKor을 각각 get kor과 set kor로 분리하면
외부에서 kor만 불러도 getter와 setter을 불러올 수 있다.
exam.kor을 호출하면 자동으로 get kor가 호출된다.
정상적으로 exam.kor을 불러왔다.

get kor, set kor와 같이 속성명처럼 사용해버리면
마치 private를 풀어준 것과 같은 효과를 지닐 수 있다.
그냥 exam.kor에 값을 더할 수도 있고, 불러올 수도 있게 된다는 뜻이다.
💡 get, set을 활용해서 kor값을 수정해보자.
exam.kor++; // 자동으로 set kor가 되어서 값이 +1 되었다. exam.kor = exam.kor+1의 모습
exam.kor = 2; // 자동으로 set kor가 되어서 값을 2로 수정했다.
console.log(exam.kor); // 자동으로 get kor가 되어서 값을 불러왔다.
console.log(exam.total()); //수정한 값 2가 적용되었다.
#static 멤버변수와 메소드
이 클래스가 생성될 때마다 새로 생기는 인스턴스 멤버변수 말고
값을 공유하는 static 멤버변수도 선언할 수 있게 되었다.
💡 주의할 점:
1. 클래스 안에서 static 변수를 불러올 때도 앞에 클래스.을 붙인다.
(마치 다른 인스턴스 멤버변수가 앞에 this.을 꼭 붙이는 것처럼)
2. static 변수이기 때문에 외부에서 호출할 때에도 앞에 클래스.을 붙여서 호출한다.
3. static 메소드는 static 멤버변수만 사용할 수 있다.
(반대로 인스턴스 메소드는 인스턴스 멤버변수만 사용할 수 있다.)
static은 클래스를 통해서 호출되기 때문에(클래스. ) 넘겨받는 인스턴스가 없다.
따라서 this라고 부를 수 있는 것이 없다.
static 변수 count를 활용해서 인스턴스가 생성될 때마다 ++시키고
이를 통해 인스턴스가 몇 개 생겼는지를 알아보자.
💡 count 속성을 활용해서 인스턴스가 몇 개 생겼는지 알아보자.
class Exam {
#kor;
#eng;
#math;
static count = 0; //static 변수 선언
constructor(kor = 0, eng = 0, math = 0) {
this.#kor = kor;
this.#eng = eng;
this.#math = math;
//인스턴스가 this를 생략할 수 없는 것처럼
//static일지라도 앞에 클래스명을 붙여주어야 한다.
Exam.count++;
}
total() {
return this.#kor + this.#eng + this.#math;
}
get kor() {
return this.#kor;
}
set kor(value) {
this.#kor = value;
}
}
//인스턴스를 2개 생성했다.
let exam = new Exam(10, 20, 30);
let newExam = new Exam(10, 20, 30);
exam.kor++; //함수인데 속성처럼 사용할 수 있게 해줌
console.log(exam.kor);
console.log(exam.total());
console.log(Exam.count); //static이기 때문에 클래스.으로 부른다.
⭕ 출력결과:
인스턴스가 생길 때마다 count를 ++하게 해서
몇 개의 인스턴스가 생성되었는지를 확인할 수 있게 되었다.

static은 인스턴스 객체가 생기든 안생기든 상관없이
class가 정의되는 최초 1회만 초기화되는 것이다.
따라서 일반 constructor에 두는 것은 바람직하지 않겠다.
그럼 어떻게 분리해서 초기화해야 할까?
이럴 때 별도의 static 블록을 만들어서 사용할 수 있다.
class를 정의할 때 최초 1회만 초기화한다.
💡 static 블록 안에서 static 초기화하기
class Exam {
#kor;
#eng;
#math;
static count;
static {
Exam.count = 100; //반드시 Exam.을 해야함
}
constructor(kor = 0, eng = 0, math = 0) {
this.#kor = kor;
this.#eng = eng;
this.#math = math;
Exam.count++; //반드시 Exam.을 해야함
}
total() {
return this.#kor + this.#eng + this.#math;
}
get kor() {
return this.#kor;
}
set kor(value) {
this.#kor = value;
}
}
===========================================
let exam = new Exam(10, 20, 30);
let newExam = new Exam(10, 20, 30);
console.log(Exam.count);
⭕ 출력결과:
count가 100으로 초기화 되었고,
인스턴스가 생성될 때마다 ++이 되어서 102가 나왔다.

static 변수와 메소드는 인스턴스 객체에서는 사용될 수 없다.
오로지 class를 통해서만 호출될 수 있다.
때문에 다음과 같은 식을 사용할 수 없다(X).
💡 static 메소드 안에서 인스턴스 변수 부르기
class Exam {
#kor;
#eng;
#math;
static count;
static {
Exam.count = 100;
}
static getKor() {
// return Exam.count; //static 변수
return this.kor; //인스턴스 변수
}
===================================
console.log(Exam.getKor());
⭕ 출력결과:
인스턴스 변수인 this.kor은 static 메소드에서 사용될 수 없다(X).
this.kor은
1. 인스턴스 내에서 정의되지 않은 메소드이며 (우리가 가진 것은 this.#kor뿐, this.kor아님)
2. 우리가 가진 this.#kor로 수정할지라도 private라서 불러올 수 없고
3. 불러올 수 있는 변수는 오로지 static 변수인 count 뿐이다.
static은 넘겨받는 인스턴스가 없다. 따라서 this라고 부를 수 있는 것이 없다.
위의 결과는 this.kor가 정의되지 않았기 때문에 undefined이다.

#private static 멤버 선언
static 멤버에도 private를 사용할 수 있다.
인스턴스 멤버와 동일하게 앞에 #을 붙여주면 된다.
💡 static 멤버변수에도 private을 붙이고
static getter/static setter로 불러오자.
class Exam {
#kor;
#eng;
#math;
static #count;
static {
Exam.#count = 100;
}
static get count() { //static getter
return Exam.#count;
}
static set count(value) { //static setter
Exam.#count = value;
}
constructor(kor = 0, eng = 0, math = 0) {
this.#kor = kor;
this.#eng = eng;
this.#math = math;
Exam.count++; //static setter count를 부른 것이다.
}
total() {
return this.#kor + this.#eng + this.#math;
}
get kor() { //인스턴스 getter
return this.#kor;
}
set kor(value) { //인스턴스 setter
this.#kor = value;
}
}
==========================================
console.log(Exam.count); //static getter를 호출
⭕ 출력결과:
count를 private으로 선언하였는데도,
static getter를 통해서 불러올 수 있게 되었다.

🚨 어 왜 여기 Exam.count에서는 #을 안써도 오류가 안나나요?
constructor(kor = 0, eng = 0, math = 0) {
this.#kor = kor;
this.#eng = eng;
this.#math = math;
Exam.count++; //static setter count를 부른 것이다.
}
counstructor안에서 불러온 Exam.count는
static 멤버변수 #count가 아니라 static 메소드 set count()이다.
※ get count, set count를 하는 순간 count의 private이 임시 해제된다고 생각하면 된다.
#상속
클래스가 생겼으니 상속이 가능해졌다.
extends 키워드를 사용해서 상속을 구현해보자.
👍 Exam 클래스를 상속받는 NewlecExam 클래스를 만들어보자.
----------------------------------------------
class Exam {
#kor;
#eng;
#math;
static #count;
static {
Exam.#count = 100;
}
static get count() {
return Exam.#count;
}
static set count(value) {
Exam.#count = value;
}
constructor(kor = 0, eng = 0, math = 0) {
this.#kor = kor;
this.#eng = eng;
this.#math = math;
Exam.count++; //setter가 없으면 오류난다.
}
total() {
return this.#kor + this.#eng + this.#math;
}
get kor() {
return this.#kor;
}
set kor(value) {
this.#kor = value;
}
}
----------------------------------------------
class NewlecExam extends Exam {
};
=============================================
let exam2 = new NewlecExam(10, 10, 10);
console.log(exam2.total());
⭕ 출력결과:
extends라는 키워드만 쓰고
NewlecExam안에 아무것도 구현하지 않았지만 total() 메소드를 호출할 수 있다.
상속을 통해 Exam의 total() 메소드가 호출되는 것

#상속에서의 생성자
자바에서는 부모의 생성자를 반드시 호출하지 않아도 자동으로 처리가 되었지만
자바스크립트에서는 반드시 자식에서는 부모 생성자를 호출해야 한다.
그래야 오류가 나지 않음
생성자와 total()메소드를 오버라이드해서 NewlecExam에서 호출해보자.
💡 생성자와 total()메소드 오버라이드 하기
class NewlecExam extends Exam {
#com;
constructor(kor, eng, math, com) {
super(kor, eng, math); //반드시 부모 생성자 호출해야함
this.#com = com;
}
total() { //오버라이드
return super.total() + this.#com;
}
};
==========================================
let exam2 = new NewlecExam(10, 10, 10, 10);
console.log(exam2.total());
⭕ 출력결과:
생성자와 total메소드에 this.#com을 추가하여서
원하는 값인 40을 출력할 수 있게 되었다.

상속관계에서의 객체를 비교하면 어떤 결과가 나올까?
💡 typeof과 instanceof를 통해 알아본 자바스크립트의 상속
let exam2 = new NewlecExam(10, 10, 10, 10);
console.log(exam2.total());
console.log(typeof exam2, typeof Exam, typeof NewlecExam);
console.log(exam instanceof Exam, exam2 instanceof Exam);
console.log(exam instanceof NewlecExam, exam2 instanceof NewlecExam);
console.log("exam hasOwn", Object.hasOwn(exam, 'total'));
console.log("exam.__proto__ hasOwn", Object.hasOwn(exam.__proto__, 'total'));
⭕ 출력결과:
*typeof
인스턴스인 exam2는 객체인 것으로 나오고,
클래스인 Exam과 NewlecExam은 함수로 나온다.
*instanceof
자식은 부모의 instance라고 나오지만 부모는 자식의 instance라고 나오지 않는다.
*Object.hasOwn
분명 class안에 메소드를 정의했지만, class안에서 total이라는 메소드는 없으며
__proto__안에 있음을 알 수 있다.

💙 마치며
1.
새롭게 배운 class 개념을 통해
기존에 만들었던 game의 ball을 캡슐화해서
다시 그려보는 시간을 가졌다.
이전의 불편해던 부분이
class와 private의 등장으로
많이 해소된 것 같다.
2.
특히 자바스크립트에서 getters/setters는
그 자체를 변수처럼 사용할 수 있어서
참 유용한 것 같다.
구태여 메소드 getKor()과 같은 형태로
호출할 필요가 없이
exam.kor로 불러올 수 있으니
마치 짝꿍의 말대로 private이 해제된 느낌이 든다.