
move() 메서드를 자식 클래스에서 다시 작성하면, 자식 인스턴스는 부모의 기능 대신 자식의 move()를 실행하게 됩니다.두 개념의 차이점을 요약하면 다음과 같습니다:
class 문법을 통해 구현할 수 있습니다.JavaScript에서는 같은 이름의 함수를 여러 번 선언해도 마지막 선언만 살아남아 이전 선언을 덮어씌웁니다. 예를 들어:
function foo(x) { console.log("첫 번째 선언"); }
function foo(x, y) { console.log("두 번째 선언"); }
foo(1); // "두 번째 선언" 출력
위 코드에서 두 번째 foo가 첫 번째 foo를 덮어씁니다. 따라서 JavaScript는 기본적으로 함수를 이름으로만 구분하기 때문에, 파라미터 개수나 타입별로 다른 함수를 선언해도 오버로딩을 직접 지원하지 않습니다.
그렇다면 오버로딩과 비슷한 효과를 내려면 어떻게 할까요? 대표적인 우회 방법은 함수 내부에서 arguments 객체나 파라미터 검사를 이용해 동적으로 분기 처리하는 것입니다. 예를 들어, 전달된 인자 개수를 검사하여 동작을 달리할 수 있습니다:
// 인자 개수에 따라 다르게 동작하는 예시 함수
function greet(name, age) {
if (arguments.length === 1) {
console.log(`${name}님, 안녕하세요!`); // 인자가 1개일 때
} else {
console.log(`${name}님은 ${age}세이시군요.`); // 인자가 2개일 때
}
}
greet("철수"); // "철수님, 안녕하세요!"
greet("영희", 25); // "영희님은 25세이시군요."
또는 ES6의 **기본 매개변수(default parameter)**나 나머지 매개변수(rest parameter) 문법을 활용할 수도 있습니다. 예를 들어 하나의 매개변수만 전달되면 정사각형 넓이를, 두 개가 전달되면 직사각형 넓이를 계산하도록 하면 일종의 오버로딩처럼 동작합니다:
function area(width, height = width) {
// height가 전달되지 않으면 width를 사용(정사각형)
return width * height;
}
console.log(area(5)); // 25 (5 * 5, 정사각형)
console.log(area(5, 3)); // 15 (5 * 3, 직사각형)
또 다른 방법으로는 객체를 인자로 받아 속성 유무에 따라 분기하거나, 메서드를 분리한 뒤 이름만 다르게 지정해서 옵션 객체 방식으로 사용하는 패턴 등이 있습니다. 하지만 기본적으로 여러 시그니처를 가진 함수를 선언하는 방식의 오버로딩은 불가능하므로, 위와 같이 내부에서 분기 처리를 직접 구현해야 합니다.
오버라이딩은 상속 관계에서 부모 메서드를 자식에서 재정의하는 것이므로, 먼저 상속(inheritance) 구조를 만들어야 합니다. JavaScript에서는 예전부터 프로토타입을 이용한 상속을 지원하며, ES6부터는 class 문법도 제공하고 있습니다. 두 가지 방법으로 살펴보겠습니다.
프로토타입 기반 상속 예시:
// 부모 클래스(생성자 함수)
function Animal() {}
Animal.prototype.move = function() {
console.log("동물이 움직여요");
};
// 자식 클래스(생성자 함수)
function Cat() {
Animal.call(this); // 부모 생성자 호출(속성 상속, 필요한 경우)
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
// 메서드 오버라이딩: 자식 프로토타입에 같은 이름의 메서드 재정의
Cat.prototype.move = function() {
console.log("고양이가 살금살금 움직여요");
};
let a = new Animal();
let c = new Cat();
a.move(); // "동물이 움직여요" (부모 메서드)
c.move(); // "고양이가 살금살금 움직여요" (자식에서 재정의한 메서드)
위 예제에서 Cat.prototype.move를 새로 정의했기 때문에, 자식 객체 c는 부모 Animal의 move 대신 자신의 메서드를 사용합니다. 이처럼 부모 클래스의 메서드를 덮어쓰면 오버라이딩이 됩니다. 필요에 따라 Animal.prototype.move.call(this)처럼 call을 사용해 부모 메서드를 호출한 뒤, 추가 기능을 덧붙일 수도 있습니다.
ES6 class 문법 예시:
class Animal {
move() {
console.log("동물이 움직여요");
}
}
class Cat extends Animal {
move() { // 오버라이딩
super.move(); // (선택) 부모 메서드 호출
console.log("고양이가 살금살금 움직여요");
}
}
const animal = new Animal();
const cat = new Cat();
animal.move(); // "동물이 움직여요"
cat.move(); // "동물이 움직여요" (super 호출) → "고양이가 살금살금 움직여요"
ES6 class를 사용해도 기본 개념은 같습니다. Cat 클래스가 Animal을 상속한 뒤, move 메서드를 자식에서 다시 정의했습니다. super.move()를 호출하면 부모의 기능을 유지하면서 추가 동작을 구현할 수 있습니다. 이때도 자식의 move가 부모 것을 덮어쓰는(overriding) 것임에 유의해야 합니다.