encapsulation: class 안에서, 밖에서 볼 수 있는 것을 나눠둔 것.
class person {
name; //속성(field)
age; //속성(field)
speak(); //행동(method)
}
class: template
object: instance of a class
Class declaration
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
speak() {
console.log(${this.name}: I'm ${this.age} years old!
);
}
}
Object 생성
const yuna = new Person('yuna', 20);
함수 출력 / 호출
console.log(yuna.name); //함수 값 출력
console.log(yuna.age); //함수 값 출력
yuna.speak(); //함수 메소드 호출
getter & setter
encapsulation
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const user1 = new User('yuna', -1);
사용자의 name과 age를 받는 Person이란 클래스가 있다고 하자.
user1에 대한 나이가 -1인 것을 볼 수 있는데, 나이는 음수일 수 없으므로 함수를 부르는 사용자가 이상한 짓을 하지 못하도록 getter & setter가 필요하다.
값을 불러오기 위한 get(){} 메소드와 값을 설정하기 위한 set(){} 메소드를 class 안에 추가하면 아래와 같은 모습이다.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
get age() {
return this._age;
}
set age(value) {
this._age = value;
}
}
class 안에 getter를 정의하는 순간,
즉 get age()라는 메소드가 생기는 순간,
constructor 안에 있는 this.age는 메모리 안에 있는 this.age라는 값을 읽어오는 게 아니라, this의 메소드 .age()를 호출하게 된다.
똑같이 class 안에 setter를 정의하는 순간,
즉 set age()라는 메소드가 생기는 순간,
constructor 안에 있는 = age는 (= equal sign)
값을 할당할 때,
set age(value) {} 메소드를 호출하게 된다.
setter 안에 this.age = value; 에도 (= equal sign)이 있는데,
이는 메모리에 값을 직접 할당하는 게 아니라
set age(value) {} 메소드를 호출한다.
이게 무한정 반복되니, 이대로 코드를 실행하면
call stack size exceeded라는 에러가 발생하게 된다.
이것을 방지하기 위해 getter와 setter 안에 쓰이는 변수 이름을 _age와 같이 조금 다르게 만든다.
getter & setter를 설정하고 난 뒤에는
이제 class 안에 총 2개의 fields가 생기게 되는데
1. name
2. _age
이다.
이제 setter 안에서 받아온 value가 음수이면 에러메세지를 출력할 수 있다.
field에는 기호가 들어간 _age가 있지만,
우리가 .age라고 호출할 수 있는 것은
그리고 .age에 값을 할당할 수 있는 것은
내부적으로 getter와 setter를 이용하기 때문이다.
field 이름 앞에 #를 붙이면 private field가 된다. 클래스 외부에서는 이 값을 읽거나 변경할 수 없게 된다.
static 키워드를 사용해서 만든
field나 method는
object 이름.field
object 이름.method() 와 같이 호출하는 것이 아니라
클래스 안에 속한 것이기 때문에
class 이름.field
class 이름.method()와 같이 호출해야 한다.
들어오는 데이터에 상관 없이 공통적으로 class에서 쓸 수 있는 field나 method를 만들 때 쓰인다. 이렇게 하면 메모리를 절약할 수 있기 때문!
class Shape {}
class Rectangle extends Shape {}
const rectangle = new Rectangle(20, 20, 'blue');
rectangle.draw(); // Shape의 메소드인 .draw()를 rectangle 안에서도 부를 수 있다. Rectangle은 Shape를 상속했기 때문에! Rectangle은 Shape의 값이나 메소드를 쓸 수 있다.
상속을 이용하면, class끼리 공통된 field나 method를 모두 다시 작성하지 않아도 계속 재사용할 수 있다. 코드 타입을 덜 해도 되는 것이다.
또한 여러 class에서 수정해야 할 것이 있으면, Shape에서만 수정하면 나머지 상속한 모든 class에서도 수정이 되므로 유지보수가 쉬워진다.
class Shape {}
class Rectangle extends Shape {}
class Triangle extends Shape {}
이 때 overriding을 이용한다.
필요한 함수들만 overriding해서 사용하면 된다.
공통적으로 정의된 method도 호출하면서, overriding된 method도 호출하고 싶다면 super.method()를 사용한다.
class Triangle extends Shape {
draw() {
super.draw(); // 부모(super)의 메소드인 Rectangle의 draw()를 호출한다.
console.log();
}
};
console.log(rectangle instanceof Rectangle);
console.log(triangle instanceof Shape);
console.log(triangle instanceof Object); // true
*특이한 점!
자바스크립트에서 만든 모든 object는 자바스크립트 자체에 있는 Object를 상속한 것이므로 마지막 줄에서 true가 나온다.
확인을 위해 커맨트나 컨트롤 키를 누른 채로 object를 클릭하면 이가 정의된 부분으로 가서 볼 수 있다.
ex)
triangle.toString(); // [object object] 출력 (쓸모 없는 데이터)
class Triangle extends Shape {
toString() {
return 'Triangle: ${this.color}'; // Triangle: blue
}
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference
정해진 데이터를 처리하는 경우에는 if문보다는 switch문이 좋다.