※ 책 [혼자 공부하는 자바스크립트]를 공부하며 기록, 정리해 놓은 노트이다.
목차
클래스의 고급 기능
상속, private 속성/메소드, 게터/세터, static 속성/메소드, 오버라이드
요약:
상속 inheritance
: 어떤 클래스(부모)가 갖고 있는 유산(속성과 메소드)을 기반으로 새로운 클래스를 만드는 것.
클래스의 선언 코드를 중복해서 작성하지 않도록 함으로써 코드의 생산 효율을 올리는 문법이다.
class 자식 클래스 extends 부모 클래스 { … }
// 부모 클래스 Rectangle
class Rectangle {
constructor (width, height) {
this.width = width;
this.height = height;
}
getPerimeter () {
return 2 * (this.width + this.height)
}
getArea () {
return this.width * this.height
}
}
// 자식 클래스 Square
class Square extends Rectangle {
constructor (length) {
super(length, length)
}
}
// 자식의 넓이 구해보기:
const square = new Square(10, 20, 50)
console.log(`정사각형의 넓이: ${square.getArea()}`)
=> 결과:
정사각형의 넓이: 100
⇒ super()는 부모의 생성자 함수를 호출하는 키워드이고,
⇒ new Square(10, 20, 50)은 잘못 본 게 아니다. 넘치는 인자를 전달해도 에러 없이 잘 작동한다.
(+ 추가)
super
키워드를 호출하면 부모 클래스의 생성자(constructor)를 호출하고, (super()
)
super
키워드를 참조하면 부모 클래스의 메서드(Method)를 호출할 수 있다. (super.getArea()
)
- 생성자 내부의
super()
는 생성자 내에서만, 그리고this
키워드를 사용하기 전에만 쓸 수 있다.
- 자식 크래스를 서브 클래스라고도 한다.
클래스 만드는 연습하기 Quiz
애플리케이션 개발자
: 프레임워크와 엔진을 활용해서 사용자를 대상으로 하는 서비스, 애플리케이션, 게임 등을 개발하는 개발자. 애플리케이션 개발자들이 프레임워크와 엔진을 활용하는 가장 기본적인 방법이 바로 상속이다.
여기까지(=’상속’)는 애플리케이션 개발자가 신경쓰는 것.
여기서부터는 프레임워크 개발자가 신경쓸 것.
프레임워크 개발자 (엔진 개발자)
: 프로그램을 개발할 때 사용되는 프레임워크를 만드는 개발자.
프레임워크 framework, 엔진 engine
: 거대한 규모의 클래스, 함수, 도구 등의 집합
쉽게 말하면, 프레임워크 개발자가 클래스를 만들어 제공하고 애플리케이션 개발자가 클래스를 사용하는 것이다. 애플리케이션 개발자는 ‘상속’ 기능을 잘 써야하고, 프레임워크 개발자는 ‘private 속성이나 static 속성, 게터/세터, 오버라이딩’ 등을 잘 알아야 한다.
private 속성과 메소드
: 클래스 내부에서만 접근할 수 있는 속성과 메소드이다. 클래스 사용자(애플리케이션 개발자)가 클래스의 속성이나 메소드를 의도하지 않은 방향으로 사용하는 것을 막고 클래스의 안정성을 확보하기 위해 나온 문법.
class 클래스 이름 { #속성 이름 #메소드 이름 () { … } }
이후에 this.#length와 같이 사용.
클래스 외부에서 square.#length = -10과 같이 변경하려고 하면 Uncaught SyntaxError: Private field '#length' must be declared in an enclosing class 발생.
클래스 외부에서 square.length = -10으로 변경한다 해도 #length 값에는 지장을 주지 못한다. square.length라는 새로운 속성이 생길 뿐.
게터와 세터
: getOO()과 setOO() 형태로 값을 확인하고 지정하는 기능을 가진 메소드
그리고 발전된 게 get 키워드와 set 키워드 (사용하는 쪽에서 편하도록):
class 클래스 이름 { get 메소드 이름 ( ) { return 값 } set 메소드 이름 (value) { } }
클래스 선언할 때(프레임워크 개발자):
클래스 선언할 때(프레임워크 개발자):
class Square {
#length
constructor(length) {
// 발전1: this.#length = length
// 발전2: getter 메소드 사용: this.setLength(length)
// 발전3: get 키워드 사용:
this.length = length // => 이 형태로 값을 지정하면 set length(length)가 호출돼서 결국 this.#length = length가 되게 된다.
}
// 원래: getLength()
get length() {
return this.#length
}
// 원래: setLength(value)
set length(value) {
if (value <= 0) {
throw '길이는 0보다 커야 합니다'
}
this.#length = value
}
// 원래: getPerimeter()
get perimeter() { return this.#length * 4 }
// 원래: getArea()
get area() { return this.#length**2 }
}
⇒ 생성자 내부에서: this.#length 아니고 그냥 this.length = length로 변경.
⇒ 메소드 이름들 앞에 get 혹은 set 키워드를 떼고 이름을 가볍게 만듬.
사용할 때(애플리케이션 개발자):
사용할 때(애플리케이션 개발자):
const squareA = new Square(10)
console.log(`한 변의 길이: ${squareA.length}`) // #length 아니고 length()가 호출됨.
console.log(`둘레: ${squareA.perimeter}`)
=> 결과:
한 변의 길이: 10
둘레: 40
⇒ 메소드를 속성처럼 사용하면 자동으로 해당 게터와 세터가 호출되게 되는 원리이다.
⇒ 예를 들면 squareA.length는 해당 게터가 호출됐다. squareA.perimeter도 마찬가지. (당연히 해당하는 이름의 게터를 선언해 놓았다는 전제 하)
static 속성과 메소드
: 인스턴스를 만들지 않고 사용할 수 있는 속성과 메소드. (=클래스 속성/메소드)(=정적 속성/메소드)
선언:
class 클래스 이름 { static 속성 = 값 static 메소드 ( ) { … } }
사용:
클래스 이름.속성 클래스 이름.메소드()
⇒ static과 private 키워드의 짬뽕도 가능하다.
⇒ static과 get/set 키워드 메소드의 짬뽕도 물론 가능.
예시
// Static 속성과 메소드 (그리고 private, set/get 키워드와의 짬뽕): class SquareStatic { #length static #counter = 0 static get counter() { return SquareStatic.#counter // 클래스 내부에서 'this'가 인스턴스를 가리켰다면, // 클래스 변수를 위한 클래스 자체를 가리키기 위해선 '클래스명'! } constructor(length) { this.length = length // 자동으로 set legnth(value)가 불리는 거, 알제? SquareStatic.#counter += 1 } // 클래스 함수로 곧바로 계산: static perimeterOf(length) { return length * 4} static areaOf(length) { return length ** 2 } // 인스턴스 함수로 받아 계산: get length() { return this.#length } // 프라이빗 length는 인스턴스가 활용하기로 함. // 프라이빗 counter는 클래스가 활요하고 있는데 반해. get perimeter() { return this.#length * 4 } get area() { return this.#length ** 2 } set length(value) { if (value <= 0) { throw '길이는 0보단 커야합니다.' } this.#length = value } } const squareB = new SquareStatic(10) const squareC = new SquareStatic(20) console.log(`\n지금까지 생성된 Square 인스턴스는 총 ${SquareStatic.counter}개입니다`) console.log(`한 변의 길이가 20인 정사각형의 둘레는 ${SquareStatic.perimeterOf(20)}`) console.log(`한 변의 길이가 20인 정사각형의 넓이는 ${squareC.area} 입니다.`) => 결과: 지금까지 생성된 Square 인스턴스는 총 2개입니다 한 변의 길이가 20인 정사각형의 둘레는 80 한 변의 길이가 20인 정사각형의 넓이는 400 입니다.
오버라이딩 overriding
: 부모가 갖고 있는 메소드와 같은 이름으로 메소드를 선언해서 덮어 쓰는 것.
부모의 메소드도 사용하고 싶다면: super.부모메소드()
자바스크립트도 최상위 클래스가 Object이고 모든 클래스가 자동으로 Object 클래스를 상속받게 된다. 따라서 Object에 정의된 toString()도 자동으로 상속되게 되는데, 내가 만든 클래스에서 toString()을 오버라이드해서 원하는 형태로 문자열로 출력하게 만들 수 있다.
alert()의 출력 형태 바꿔보기
// alert의 출력형태 바꿔보기
// + 클래스를 문자열과 결합해서 출력하면?
class Pet {
constructor(name, age) {
this.name = name
this.age = age
}
toString() { return `이름은 ${this.name}이고\n나이는 ${this.age}살`}
}
const pet = new Pet('바람', 3)
alert(pet)
console.log(pet + '')
console.log(pet)
=> 결과:
이름은 바람이고
나이는 3살 // (alert창으로)
이름은 바람이고
나이는 3살
Pet {name: '바람', age: 3}
⇒ Pet 내에 toString() 메소드 오버라이딩이 잘 되었다. alert()과 문자열 결합 (+) 연산자가 내부적으로 toString()을 호출하게 될 때 오버라이드한 toString()이 불리게 된다.
⇒ alert()은 매개변수로 받은 자료를 문자열로 바꾼 뒤(이 때 toString()이 쓰임) 출력하고, 문자열 결합 연산자도 문자열이 아닌 다른 쪽 객체의 toString() 메소드를 호출하고 계산을 진행하는 것.
⇒ 둘 다 한마디로, 클래스 객체가 문자열로 바뀔 때면 내부적으로 toString()을 호출해서 대신 내보내는 원리이다: alert(객체)에서 alert(): “객체를 문자열로 바꿔라” ⇒ 객체: “toString() 네가 나가라” 객체 + “”에서 결합 연산자가: “객체야 네가 문자열로 변해라” ⇒ 객체: “toString() 네가 나가라”
참고:
[책] 혼자 공부하는 자바스크립트 - 윤인성
Uploaded by N2T