개발일지

생성자 함수? class아니냐? 본문

개발일지/Javascript

생성자 함수? class아니냐?

Seobe95 2022. 9. 16. 01:23

oneook님의 썸네일 메이커 (https://wonkooklee.github.io/thumbnail_maker/)

생성자 함수? class아니냐?

이번에 프로그래머스 강의 중 하나인 Vanilla JS 스터디에 참여하게 되면서, 리액트나 뷰, 스벨트와 같은 라이브러리를 사용하지 않고 간단한 기능들을 만들어 보고 있다.

 

그러던 중.. 

 

 

멘토님께서 해당 부분을 설명해 주시면서, function을 사용하면서도 new를 붙여서 구현하는데 이를 생성자 함수라고 설명하셨고, 생성자 함수에 관련된 내용도 짧게 설명해주셨다.

 

그놈의 붕어빵,, 출처(https://wikidocs.net/165339)

class는 흔히들 붕어빵 찍어내는 틀이라고 생각하라던 강의들과 설명들이 많았다. 생성자 함수 또란 이와 비슷한 개념으로, 조금 찾아보니 class와 생성자 함수는 많은 점이 닮았지만, 조금은 다르게 동작하며 class에서는 사용할 수 있는 기능이 생성자 함수에서는 사용할 수 없다는 등의 차이점이 있었다.

 

// 생성자 함수
function Person1 (name, age) {
    this.name = name;
    this.age = age;
    
    this.introduce = function(){
    	console.log(`안녕하세요 저는 ${name}이고 ${age}살 입니다!`);
    }
}

// class
class Person2 {
    constructor (name, age) {
    	this.name = name;
        this.age = age;
    }
    
    introduce() {
    	console.log(`안녕하세요 저는 ${this.name}이고 ${this.age}살 입니다!`);
    }
}

const test1 = new Person1("서비", 28);
const test2 = new Person2("서비", 28);

test1.introduce(); // 출력값 : 안녕하세요 저는 서비이고 28살 입니다!
test2.introduce(); // 출력값 : 안녕하세요 저는 서비이고 28살 입니다!

생긴 것도 비슷하다

 

위의 예시처럼 생성자 함수, class는 결국 프로토타입 기반의 인스턴스(객체)를 생성한다.

쉽게 이야기하자면, 생성자 함수던 class던 결국 객체를 찍어내기 위해 사용되는 방법들 중 하나인 셈이다.

 

위의 예시를 보면, 하나의 함수에 다른 파라미터만 입력하면 수많은 사람들의 소개를 출력할 수 있다!

생성자 함수 주의점

1. 생성자함수의 이름의 첫 글자는 대문자로 시작(파스칼 케이스)한다.

2. new 연산자를 붙여서 사용한다.

 

사실 1번 같은 경우는 암묵적으로 지키는 룰이지만, 2번인 new 연산자가 붙어있지 않다면 생성자 함수가 아닌 일반 함수로 호출하게 된다.

 

const testWithNew = new Person("seobe", 28);
const testWithoutNew = Person("seobe", 28);

console.log(testWithNew) // Person {name: "seobe", age: 28, inrtoduce: function }
console.log(testWithoutNew) // undefined

 

위의 경우 testWithNew는 new 연산자를 붙여서 호출하고, testWithoutNew는 new 연산자 없이 호출했다.

 

여기서 testWithNew의 경우 우리가 원하는 객체를 보여주고 있지만, testWithoutNew는 undefined가 나오는데, 이 이유는 new 연산자가 붙게 되면 함수 내 this는 생성자 함수인 Person가 생성할 인스턴스(객체)를 가르키게 된다. (모던 자바스크립트 Deep Dive 중)

 

이와 반대로 testWithoutNew는 일반 함수로 호출되어 Person 함수 내 this가 window를 가리켜서 undefined가 나오게 된다.

 

이런 점 때문에 생성자 함수가 new 연산자와 사용되지 않는 경우를 대비해야 하는데, 두 가지의 방법이 있다.

 

첫 번째로는 new.target을 사용하는 방법이다.

생성자 함수를 사용하는 경우 함수 내부에서 new.target을 사용하면 new 연산자와 함께 사용되었는지 확인 할 수 있다.

 

function Test (value) {
    if(!new.target) {
    	return throw new Error("new 연산자를 붙여야 합니다!")
    }
	console.log("new 연산자와 함께입니다!")
    ...
}

Test();     // "new 연산자를 붙여야 합니다!"
new Test(); // "new 연산자와 함께입니다!"

// 더 나아가서
function Test (value) {
    if(!new.target) {
    	return new Test(value); // 재귀함수를 이용해서 new 연산자를 붙여주기
    }
    this.test = value;
}

 

단, new.target은 ES6에서 지원하기 때문에, IE에서 작동이 되지 않는다. (IE가 중단되었는데, 상관없지 않을까,,,?)

 

두 번째로는 스코프 세이프 생성자 패턴이다.

new 연산자와 함께 생성자 함수에 의해 생성된 인스턴스(객체)는 생성자 함수와 연결되어 있는데, 이를 확인하기 위해 instanceof 연산자를 사용하여 new 연산자가 사용되었는지를 확인할 수 있다. (프로토타입의 개념이 필요한데, 다음 기회에 정리하도록 하겠음,,)

function Test (value) {
    if(!(this instanceof Test)) {
        throw new Error("new 연산자를 붙여야 합니다!")
    }
    console.log("new 연산자와 함께입니다!");
    ...
}

Test()      // "new 연산자를 붙여야 합니다!"
new Test()  // "new 연산자와 함께입니다!"

위의 경우는 다른 제약사항이 없기 때문에, new.target을 이용하는 것 보다는 스코프 세이프 생성자 패턴을 사용하는 것이 나을 수 있겠지만, IE의 지원이 중단된 상태이므로 딱히 상관이 없지 않을까 싶다. (이럴거면 class 쓰지,,)

class와의 차이점

모던 자바스크립트 Deep Dive에 따르면 생성자 함수와 class와의 차이점은 다음과 같다.

  1. class는 new 연산자가 없으면 에러가 발생하지만 생성자 함수는 일반 함수로 호출된다.
  2. class는 상속을 지원하는 extends와 super 키워드가 있지만, 생성자 함수에는 지원되지 않는다.
  3. class는 호이스팅이 발생하지 않는 것처럼 작동하지만, 생성자 함수에서는 호이스팅이 발생한다.
  4. class 내 모든 코드는 strict mode가 지정되어서 실행되고 해제할 수 없지만, 생성자 함수는 암묵적으로 strict mode가 지정되어 있지 않지만, 함수 내부에 use strict를 선언하면 된다

등이 있다.

마무리

멘토님이 말씀해주셨던 내용 중 가장 중요하다고 생각했던 것은, 결국 class를 생성자 함수로도 바꿔 작성할 수 있고, 그 반대의 경우도 자유자제로 할 수 있어야 한다는 점이다.

 

단순히 어떤게 더 좋고 나쁘고를 떠나서 그때그때의 상황에 맞는 코드를 작성할 수 있어야 하고, 그 실력을 만들기 위해서는 기본이 중요하다고 생각하는 요즘이다.