class에서 static과 prototype의 차이
nodejs에서 class는 인스턴스를 생성하는 방식중 하나 입니다. class를 사용해서 인스턴스를 생성하는 경우와 생성자 함수를 통해 인스턴스를 생성하는 경우의 차이에 대해 비교해보고, class에서 메소드를 정의하는 방법에 대해서 이야기 해보려 합니다.
생성자 함수를 이용한 인스턴스 생성
function User(name, age) {
this.name = name;
this.age = age;
this.sayHi = function (){
console.log('hello~!');
return;
};
}
생성자 함수를 활용하여 인스턴스를 생성하는 방법은 위의 방법과 같습니다. 생성자 함수를 사용해 인스턴스를 생성하는 방법은 js에서 인스턴스를 생성하는 가장 기초적인 방법입니다. 다만 위의 방법에서는 한가지 문제점이 발생합니다. 불필요한 메모리의 낭비가 발생합니다.
위의 생성자 함수를 이용해서 생성한 User인스턴스의 내부를 확인해보면. 인스턴스 내부에 sayHi라는 메서드가 있는것을 확인할 수 있습니다.
불필요한 메모리의 낭비가 발생하는 이유는 위의 User
생성자 함수를 이용해서 인스턴스를 10개 생성한다고 가정을 하는 경우에, sayHi라는 함수는 10개의 인스턴스에 하나씩 총 10개의 함수가 생성됩니다.
javascript는 실행컨택스트를 생성하며 함수객체를 평가합니다. 이때 식별자에 할당된 값들은 heap메모리의 영역에 올라가게 됩니다. 위와 같은 경우에는 heap메모리에 동일한 기능을 하는 함수 10가지가 저장되어 있는 상황입니다.
생성자 함수와 프로토타입 메소드 선언을 통한 인스턴스 생성
이러한 중복으로 인한 메모리의 낭비를 방지하게 위해 sayHi함수를 user 프로토타입의 함수로 정의해주면 메모리의 낭비를 해결할 수 있습니다. 코드는 아래와 같습니다.
function User(name, age) {
this.name = name;
this.age = age;
User.prototype.sayHi = function ()
console.log('hello~!');
return;
};
}
위의 방식으로 인스턴스를 생성하면 인스턴스에 sayHi라는 메서드가 존재하는것이 아니라 프로토타입의 메서드로 정의 되는것을 확인할 수 있습니다.
프로토 타입의 메서드로 정의가 된다면 정의가 되고나면 이후 인스턴스에서는 프로토 체인을 통해 sayHi함수를 찾아서 활용하기 떄문에 불필요한 메모리의 낭비가 발생하지 않을 수 있습니다. 여기서 나온 프로토 체인의 경우 프로토타입에 관한 정리를 할때 정리 할 생각입니다.
class를 통한 인스턴스 생성
class는 초기 javascript에는 없었던 내용입니다. ES6이후 class라는 문법이 도입 되었습니다. 다시 한번 강조하자면 class는 새로운것이 아니라 인스턴스를 생성하는 방법중의 하나 입니다.
위의 User인스턴스를 생성하는 class를 예시로 들어보겠습니다.
class User {
constructor(name, age){
this.name = name;
this.age = age;
}
sayHi() {
console.log('hello~!');
return;
}
}
위의 class문법을 통해 생성한 인스턴스의 내부를 확인해보겠습니다.
생성자 함수와 달리 sayHi 함수를 정의해주면 자동으로 프로토타입의 메서드로 정의가 되는것을 볼 수 있습니다.
이제 class를 통한 인스턴스 생성중 class에 메소드를 정의하는 방법을 조금 변형시켜 보겠습니다.
class 메서드 표현식으로 선언
class User {
constructor(name, age){
this.name = name;
this.age = age;
}
sayHi = () => {
console.log('hello~!');
return;
}
}
위와 같이 선언후 인스턴스를 생성하면 sayHi는 인스턴스의 메서드로 정의가 될까요 프로토타입의 메서드로 정의가 될까요?
실행 해보겠습니다.
sayHi메서드는 인스턴스에 생성된 것을 볼 수 있습니다. 이러한 현상이 나타나는 이유는 메서드를 선언하는데 있어 함수 표현식의 방법을 사용했기 떄문입니다. sayHi라는 식별자를 선언하게 되면 이 식별자는 결국 인스턴스의 프로퍼티로 생성이 되기 때문입니다. 이렇게 되면 결국 우리가 피하려고 했던 _불필요한 메모리 낭비_가 일어나고 있는 것입니다.
static을 사용한 class 메서드 선언
class User {
constructor(name, age){
this.name = name;
this.age = age;
}
static sayHi = () => {
console.log('hello~!');
return;
}
}
또는
class User {
constructor(name, age){
this.name = name;
this.age = age;
}
static sayHi() {
console.log('hello~!');
return;
}
}
위의 class 생성자를 통해 인스턴스를 생성하면 sayHi메서드는 인스턴스에 프로퍼티 일까요? 프로토타입의 프로퍼티 일까요? 두가지 모두 틀렸습니다. 정답은 constructor의 프로퍼티로 존재하게 됩니다. 아래 사진을 보시죠.
sayHi메서드는 프로토 타입의 constructor프로퍼티가 가리키는 곳에 존재 합니다. 여기서 나오는 constructor와 프로토타입은 프로토 타입을 따로 정리하면서 설명하겠습니다. constructor의 프로퍼티는 인스턴스의 메서드로 호출할 수 없습니다. 왜냐하면 인스턴스의 프로토 체인상에 존재하는 메서드가 아니기 때문입니다. 따라서 sayHi메서드를 호출하기 위해서는 User.sayHi()
로 호출해야 합니다.
정리
- static을 활용하여 생성자 함수의 메서드 또는 식별자를 생성할 수 있다.
- 프로토타입의 메서드로 정의하기 위해서는 ES6의 함수 축약형 함수 선언방식을 사용해야 한다.
- class에서 메서드를 함수 표현식으로 정의하게 되면 식별자가 선언이 되고 해당 식별자에 함수를 매핑해주기 떄문에 프로토 타입의 메서드가 아닌 인스턴스의 메서드로 정의된다.
- 인스턴스의 메서드는 프로토체인을 통해 프로토타입 또는 프로토타입의 프로토타입의 메서드를 이용할 수 있다. 하지만 생성자 함수의 메서드는 인스턴스를 이용해 사용할 수 없다.
긴글 읽어주셔서 감사하며, 틀린부분이 있다면 언제든지 지적해주시면 감사하겠습니다.