기본 형태: class 클래스 이름 extends 부모클래스 이름 { }
상속은 이름처럼 어떤 클래스가 가지고 있는 유산 (속성과 메소드) 을 다른 클래스에게 물려주는 형태로 사용한다.
이때 유산을 주는 클래스를 "부모 클래스 (parent class)" , 유산을 받는 클래스를 "자식 클래스 (child class)" 라고 한다.
예시:
<script>
class Rectangle {
// 1. 사각형을 정의하는 클래스 생성
constructor(width, height) {
this.width = width;
this.height = height;
}
getPerimeter() {
return 2 * (this.width + this.height);
}
// 2. 사각형의 둘레를 구할 수 있는 함수를 선언한다.
// 2-1. 해당 함수는 Rectangle의 함수형 속성인 메소드이다.
getArea() {
return this.width * this.height;
}
// 3. 사각형의 넓이를 구할 수 있는 함수 또한 메소드로 추가해둔다.
}
class Square extends Rectangle {
// 4. Square라는 새로운 클래스를 생성할 때, Rectangle로부터 메소드를 상속 받는다.
constructor(length) {
super(length, length);
// 5. 부모의 생성자 함수 (Rectangle 클래스의 constructor)를 호출한다.
// 5-1. super()를 이용해서 부모의 생성자 함수를 호출하면, square의 매개변수인 "length"가 Rectangle의 width, height와 동일한 속성이 된다.
}
}
const square = new Square(20, 20);
console.log(`정사각형의 둘레: ${square.getPerimeter()}`);
console.log(`정사각형의 넓이: ${square.getArea()}`);
// 6. Square 클래스의 경우, getPerimeter(), getArea() 메소드를 따로 선언하지 않았어도 상속 받았기 때문에 사용할 수 있다.
</script>
class 클래스 이름{
#속성 이름
#메소드 이름 () {}
}
<script>
class Square {
#length;
// 1. 미리 특정 속성 (여기선 length) 을 private 속성으로 사용하겠다고 선언한다.
constructor(length) {
if (length <= 0) {
throw "길이는 0보다 커야 합니다.";
// 2. 다른 사용자가 의도와 다르게 코드를 사용하지 못 하도록 예외 처리를 하고,
}
this.#length = length;
// 2-1. constructor의 (length)가 private 속성으로 지정한 #length와 같다는 것을 명시한다.
}
getPerimeter() {
return 4 * this.#length;
}
getArea() {
return this.#length * this.#length;
}
}
const square = new Square(10);
console.log(`정사각형의 둘레: ${square.getPerimeter()}`);
console.log(`정사각형의 넓이: ${square.getArea()}`);
</script>
만약 여기서 사용자가 square의 length
속성을 변경해도, 클래스 내부에서 사용하고 있는 속성은 #length
속성이기 때문에 결과에 영향을 주지 않는다.
클래스 외부에서 #length
속성을 사용해 값을 변경하려고 해도, 외부에서 변경할 수 없다는 점 때문에 에러 메세지가 출력됩니다.
Uncaught SyntaxError: Private field '#length' must be declared in an enclosing class
<script>
class Square {
#length;
// 1. 미리 특정 속성 (여기선 length) 을 private 속성으로 사용하겠다고 선언한다.
constructor(length) {
this.setLength(length);
}
setLength(value) { // 속성에 걊을 지정하는 메소드를 세터라고 한다.
if (value <= 0) {
throw "길이는 0보다 커야 합니다.";
// 2. 다른 사용자가 의도와 다르게 코드를 사용하지 못 하도록 예외 처리를 하고,
}
this.#length = value;
// 2-1. constructor의 (length)가 private 속성으로 지정한 #length와 같다는 것을 명시한다.
}
getLength(value) { // 속성값을 확인할 때 사용하는 메소드를 게터라고 한다.
return this.#length;
}
getPerimeter() {
return 4 * this.#length;
}
getArea() {
return this.#length * this.#length;
}
}
const squareA = new Square(10);
console.log(`한 변의 길이는 ${squareA.getLength()}입니다.`);
console.log(`둘레: ${squareA.perimeter}`);
console.log(`넓이: ${squareA.area}`)
// 속성을 사용하는 형태로 사용하면 자동적으로 게터와 세터가 호출된다.
square.setLength(-10);
</script>
프레임워크 개발자들은 더 효율적으로 프레임워크를 개발할 수 있게 다양한 패턴을 고안한다.
여러 디자인 패턴을 활용하기 위해서 문법들이 추가되는데, 비교적 최근에 추가된 문법으로는 "static 속성" 과 "static 메소드" 가 있다.
class 클래스 이름 {
static 속성 = 값
static 메소드 () {
}
}
static 속성과 메소드는 인스턴스를 만들지 않고 사용할 수 있는 속성과 메소드다.
prototype
이 아닌 클래스 함수 자체에 메서드를 설정하는 것을 정적 메소드라고 부른다.
일반적인 변수와 함수처럼 사용이 가능하기 때문에 클래스 이름 뒤에 점을 찍고 사용하고, 클래스 안에서 static
키워드를 붙여 만들 수 있다.
<script>
class Square {
#length;
static #counter = 0; // private 속성과 static 속성은 한번에 적용될 수 있다.
static get counter() {
return Square.#counter;
}
constructor(length) {
this.length = length;
Square.#counter += 1;
}
static perimeterOf(length) {
return length * 4;
}
static areaOf(length) {
return length * length;
}
get length() {
return this.#length;
}
get perimeterOf() {
return this.#length * 4;
}
get area() {
return this.#length * this.#length;
}
set length(length) {
if (length <= 0) {
throw "길이는 0보다 커야 합니다.";
}
this.#length = length;
}
}
const squareA = new Square(10);
const squareB = new Square(10);
const squareC = new Square(10);
console.log(`지금까지 생성된 Square 인스턴스는 ${Square.counter}개입니다.`);
console.log(
`한 변의 길이가 20인 정사각형의 둘레는 ${Square.perimeterOf(20)}입니다.`
);
console.log(
`한 변의 길이가 30인 정사각형의 넓이는 ${Square.areaOf(30)}입니다.`
);
</script>
<script>
class LifeCyle {
// 1. 새로운 부모 클래스를 생성
call() {
// 2. 부모 클래스 LifeCyle 내부에 있는 함수로서, 호출 시 a,b,c 함수를 각각 추가로 출력한다.
this.a();
this.b();
this.c();
}
a() {
console.log("a() 메소드를 호출합니다.");
}
b() {
console.log("b() 메소드를 호출합니다.");
}
c() {
console.log("c() 메소드를 호출합니다.");
}
}
new LifeCyle().call();
// 3. 부모 클래스를 호출할 경우, 부모 클래스 내부에 작성되어 있는 내용이 동일하게 출력된다.
/*
a() 메소드를 호출합니다. a.html:18
b() 메소드를 호출합니다. a.html:21
c() 메소드를 호출합니다. a.html:24
*/
class Child extends LifeCyle {
// 4. 자식 클래스를 선언하고, extends 키워드를 사용해서 LifeCycle 클래스 내부에 있는 함수를 사용한다.
a() {
console.log("자식의 a() 메소드입니다.");
// 5. 자식 클래스 내부에서 부모 클래스 내부에 있던 함수를 수정한다.
}
}
new Child().call();
// 6. 자식 클래스 내부에 있는 함수를 호출한다.
/*
자식의 a() 메소드입니다. a:html:32
b() 메소드를 호출합니다. a:html:21
c() 메소드를 호출합니다. a:html:24
*/
</script>
super.메소드()
형태의 코드를 사용한다.class Child extends LifeCyle {
a() {
super.a();
// 1. 부모의 a() 메소드를 실행한다.
console.log("자식의 a() 메소드입니다.");
}
}
new Child().call();
// 2. 자식 클래스 내부에 있는 함수를 호출한다.
/*
a() 메소드를 호출합니다. a.html:20
자식의 a() 메소드입니다. a:html:32
b() 메소드를 호출합니다. a:html:23
c() 메소드를 호출합니다. a:html:26
*/
toString()
이라는 이름으로 메소드를 만들면 Object 클래스에 있던 toString()
메소드를 오버라이드한다.<script>
class Pet {
constructor(name, age) {
this.name = name;
this.age = age;
}
toString() {
return `이름: ${this.name} \n나이: ${this.age}살`;
}
// 1. 내부적으로 문자열로 반환되게 만든다
}
const pet = new Pet("명랑", 7);
alert(pet);
// 2. "명랑", 7 이라는 정보가 객체가 아니라 문자열로 반환된다.
console.log(pet + "");
// 3. 콘솔에도 문자열 결합 연산자를 호출할 때도 toString()를 이용해서 바꾼 형태로 출력된다
</script>
alert()
함수는 매개변수로 받은 자료를 문자열로 바꾼 후 출력했다.toString()
메소드를 오버라이드 했기 때문에 문자열로 출력된다.출처: