언어/JavaScript
#7 클래스(Class)
Scala0114
2022. 2. 21. 12:52
1. 정의
// 클래스 정의
class Student {
constructor(id, name, grade) {
this.id = id;
this.name = name;
this.grade = grade;
}
}
// 클래스의 생성자를 호출하여 객체 생성
// new <클래스명>(<생성자에 넘겨줄 인자>); 의 형태로
// 클래스에 정의된 생성자(constructor)를 호출하여 객체를 생성할 수 있음
student_1 = new Student(1, "David", 3);
// 클래스 출력
// "{'id':1,'name':'David','grade':3}"
console.log(JSON.stringify(student_1));
- class 키워드를 사용하여 class <클래스명> { ... } 의 형태로 클래스를 정의할 수 있음
- class 내부에 생성자(constructor)를 정의하여 이 생성자를 호출하는 것으로 객체를 생성할 수 있음
- 자주 쓰이는 형식의 객체는 클래스를 정의해두면 보다 간편하게 생성, 관리할 수 있음
2. protected property 와 accessor property(getter & setter)
// 클래스 정의
class Student {
// 객체의 데이터 속성(property)들을 앞에 언더바(_)를 붙일경우 (protected)
// 이 값을 접근자 프로퍼티를 사용하지않고 외부에서 직접 접근해선 안된다는 표시가 됨
// 정말로 외부접근이 불가능한 것은 아니지만 일종의 약속에 가까움
constructor(id, name, grade) {
this._id = id;
this._name = name;
this._grade = grade;
}
// 접근자 속성(Accessor Property)을 정의하여 사용자가 필드에
// 제한된 방식으로만 접근할 수 있도록 허용해줄 수 있음
// getter
get id() {
return this._id;
}
get name() {
return this._name;
}
get grade() {
return this._grade;
}
get info() {
return `studnet_id: ${this._id} | name: ${this._name} | grade: ${this._grade}`
}
// setter
set id(changedId) {
this._id = changedId;
}
set name(changedName) {
this._name = changedName;
}
set grade(changedGrade) {
this._grade = changedGrade;
}
}
// 객체 생성
student_1 = new Student(2, 'Tom', 1);
// id, name, grade 등의 데이터 속성(property)은 모두 언더바를 붙여 선언했고
// info라는 속성은 아예 존재하지도 않지만
// 접근자 프로퍼티인 getter와 setter를정의해두었기에
// 마치 실제로 존재하는 데이터속성에 접근하듯 접근할 수 있음
// studnet_1의 id, 이름, 학년을 각각출력
console.log(student_1.id); // 2
console.log(student_1.name); // "Tom"
console.log(student_1.grade); // 3
// student_1의 info를 출력
console.log(student_1.info); // "studnet_id: 2 | name: Tom | grade: 1"
// student_1의 name 속성값 변경
student_1.name = 'Benn';
// student_1의 info를 출력
console.log(student_1.info); // "studnet_id: 2 | name: Benn | grade: 1"
- 클래스 외부에서 클래스의 데이터 속성(data property) 혹은 필드(field)에 직접 접근을 허용하는 것은
보안적으로 권장되지 않기 때문에 클래스의 속성은 앞에 언더바를 붙여 protected하게 정의하는 것이 일반적
(다만 이는 시스템적으로 보호되는 것이 아닌 일종의 약속이기 때문에 외부접근 자체는 가능하며
상속 관계에서 접근이 가능한 필드를 선언하는 Java의 protected와는 개념이 다름) - get <접근자 속성 이름>() { <반환값> } 의 형태로 getter를 정의하면 해당 클래스로 생성한 객체에서는
<객체>.<접근자 속성 이름>으로 마치 존재하는 데이터 속성을 참조하듯이 반환값을 얻을 수 있음 - set <접근자 속성 이름>(<값>) { ... } 의 형태로 setter를 정의하면 해당 클래스로 생성한 객체에서는
<객체>.<접근자 속성 이름> = <값> 의 형태로 존재하는 데이터 속성의 값을 직접 변경하듯 값의 변경이 가능함 - 만약 특정 속성을 읽기 전용으로 만들고 싶다면 단순히 setter를 정의하지 않으면 해결된다.
3. private
// 클래스 정의
class Student {
// #을 붙인 private 필드를 선언
#id;
#name;
#grade;
constructor(id, name, grade) {
this.#id = id;
this.#name = name;
this.#grade = grade;
}
// 접근자 속성(Accessor Property)을 정의하여 사용자가 필드에
// 제한된 방식으로만 접근할 수 있도록 허용해줄 수 있음
// getter
get id() {
return this.#id;
}
get name() {
return this.#name;
}
get grade() {
return this.#grade;
}
get info() {
return `studnet_id: ${this.#id} | name: ${this.#name} | grade: ${this.#grade}`
}
// setter
set id(changedId) {
this.#id = changedId;
}
set name(changedName) {
this.#name = changedName;
}
set grade(changedGrade) {
this.#grade = changedGrade;
}
}
// 객체 생성
student_1 = new Student(2, 'Tom', 1);
/* 접근자 프로퍼티를 사용할경우 private 필드에도 정상적으로 접근이 가능 */
// studnet_1의 id, 이름, 학년을 각각출력
console.log(student_1.id); // 2
console.log(student_1.name); // "Tom"
console.log(student_1.grade); // 3
// student_1의 info를 출력
console.log(student_1.info); // "studnet_id: 2 | name: Tom | grade: 1"
// student_1의 name 속성값 변경
student_1.name = 'Benn';
// student_1의 info를 출력
console.log(student_1.info); // "studnet_id: 2 | name: Benn | grade: 1"
/* private 속성에는 직접 접근이 시스템적으로 불가능 */
// 직접 private 속성에 접근하려할 경우 문법오류 발생
console.log(student_1.#id);
console.log(student_1.#name);
console.log(student_1.#grade);
- #을 붙여 시스템적으로 외부에서 접근 불가능한 private 필드를 선언할 수 있음
- 개발자들간의 약속일뿐 실질적인 효력은 없는 protected 필드와 달리 문법적으로 강요되는 것이 장점
- 하지만 상속관계에 있는 클래스에서도 접근 불가능하기 때문에 private 필드는 것은 과한 경우가 많음
=> protected가 시스템적인 효력이 없음에도 사용되는 이유
4. 메소드(Method)
// 클래스 정의
class Student {
constructor(id, name, grade) {
this._id = id;
this._name = name;
this._grade = grade;
}
// 일반 메소드
introduce() {
return `My name is ${this._name} and I'm in ${this._grade} grade. My student_id is ${this._id}`;
}
// static 메소드
static classIntroduce() {
return 'This is Student class. It has three data property, id, name, and grade.';
}
}
// 객체 생성
student = new Student(1, "David", 2);
// 일반메소드의 경우 클래스 자체에서의 호출은 불가능하며 생성된 객체(인스턴스)를 통해서만 호출가능
// Student.introduce() => 오류 발생
console.log(student.introduce())
// static 메소드의 경우 클래스 자체에서의 호출이 가능하며 생성된 객체에서는 호출 불가능
console.log(Student.classIntroduce())
// student.classIntroduce() => 오류 발생
- 객체에 대한 설명에서도 언급했던 객체 내부의 함수인 메소드는 클래스에서도 정의 가능
- 클래스의 메소드는 static 메소드와 non-static 메소드(일반 메소드)로 분류됨
- static 메소드의 경우 클래스에서 직접 호출 가능한 메소드로, 인스턴스를 생성하지 않아도 호출 가능
=> 단, 인스턴스 생성 후에 생기는 값들, 즉 생성자(constructor)에서 선언되는 값 등에는 접근 불가능함 - non-static 메소드의 경우 클래스에서는 직접 호출 불가능하며 생성된 인스턴스에서만 호출 가능
=> 객체의 메소드를 정의할 때는 non-static으로 정의해야함.