티스토리 뷰

안녕하세요. 좋아요요정입니다! 

이전 객체편에서 객체 리터럴 {}을 활용하여 쉽고 빠르게 객체 인스턴스를 생성하는 방법을 살펴보았습니다.

이번엔 자바스크립트에서 객체를 생성하는 방법 중 하나인 생성자 함수를 살펴보겠습니다.

 

 


생성자 함수

자바스크립트의 함수는 객체를 생성하기 위해서도 사용할 수 있습니다. 

생성자 함수는 생성자 함수의 타입에 맞는 동일한 프로퍼티를 가진 여러개의 객체 인스턴스를 생성할 때 사용할 수 있습니다. 

객체 인스턴스는 객체 모양의 데이터 구조로 실제 저장공간에 할당된 실체를 뜻합니다. 

 

 

 

먼저, 생성자 함수가 필요한 경우로는 어떤 경우가 있을지 예를 들어보겠습니다.

여기 동물병원에서 진료받은 동물들의 정보를 기록하는 배열이 있습니다.

동물의 개인코드와 이름, 나이 그리고 해가 지나면 나이를 1살 올려주는 메서드가 있습니다.

 

1. 객체를 매번 생성하는 방법

const cats = [];

const newCat = {};
newCat.code = "CAT-2022-0101";
newCat.name = 'cookie';
newCat.age = 3;
newCat.upAge = function() {
  this.age +=1;
}
cats.push(newCat)

const newCat2 = {};
newCat2.code = "CAT-2022-0102";
newCat2.name = 'latte';
newCat2.age = 3;
newCat2.upAge = function() {
  this.age +=1;
}
cats.push(newCat2)

1번 객체를 매번 생성하는 경우 너무 반복되는 코드작성이 필요하겠죠? 

그리고 매번 직접 작성하는 경우 오타가 타거나 프로퍼티가 실수로 누락될 수도 있습니다.

그리고 계속해서 메모리에 변수가 생성되는 모습을 확인할 수 있습니다.

 

 

2. 객체를 생성하는 함수를 만드는 방법

const cats = [];

function getCat(code, name, age) {
  const newCat = {
    code, name, age
  }
  newCat.upAge = function() {
    this.age +=1;
  }
  return newCat;
}
cats.push(getCat('CAT-2022-0103', 'mayo', 3))
cats.push(getCat('CAT-2022-0104', 'mayo', 1))

 

2번은 객체리터럴로 생성된 객체를 반환하는 함수입니다. 새로운 변수는 함수 스코프 실행 컨텍스트 안에서 지역변수로 선언된 뒤 사라지기 때문에 객체 리터럴만 반환받을 수 있습니다.

그 후 저장하면 지역변수는 함수 스코프와 함께 소멸되고, 힙메모리에 저장된 객체 리터럴의 힙주소를 배열에 저장할 수 있습니다. 

깔끔해 보이지만..  같은 모양의 객체를 여러번 생성해야 할 때 생각해보아야할 문제가 하나 있습니다. 

 

 

바로 동일한 기능의 코드를 가진 메서드를 계속 생성하는 문제입니다. 

데이터가 점점 늘어날수록 동일한 기능을 가진 함수가 반복되어 메모리를 차지하게 됩니다. 

 

 

 

 

3. 생성자 함수로 객체 생성하는 방법 💕 

위의 예시와 같이 동일한 형태의 객체에 동일한 메서드가 계속 필요할 경우, 생성자 함수를 사용하여 메모리를 효율적으로 관리할 수 있습니다. 

 

 

1. 생성자 함수를 생성

function AddCat(code, name, age) {
	this.code = code;
	this.name = name;
	this.age = age;

  AddCat.prototype.upAge = function () {
    this.age +=1; 
  }
}

생성자 함수를 생성할 때에는 function 선언문과 첫 낱말을 대문자로 시작하는 파스칼 케이스를 사용하여 식별자로 선언해줍니다.

(대문자가 아니어도 작동하지만 식별을 위해 대문자로 시작하는 식별자로 등록합니다.)

우선 코드 내부를 살펴보면 this키워드를 통해 매개변수를 저장해주고, AddCat의 prototype에 메소드를 등록했습니다. 

 

 

이때의  this는 무엇을 가리키고 있을까요?

함수 내부에서 출력해보겠습니다.

 this는 생성자 함수로 만들어진 객체 인스턴스 자신을 표현합니다.

생성자 함수가 실행되면 생성자 함수는 객체 인스턴스를 초기화하여 생성해주고, 자바스크립트 엔진에 의하여 this 객체가 생성됩니다. 함수 내부에 작성된 코드를 진행한 뒤 객체 인스턴스, this 인스턴스가 반환됩니다. 

이때 함수 내부에 return문이 있을 경우 this가 아닌 return문을 반환하게 됩니다.

따라서 생성자 함수의 기본 동작을 유지하기 위해서는 생성자 함수 내부에서의 return문 사용을 지양해야 합니다. 

 

 

 

 

2. 생성자 함수를 사용해 객체 생성

const newCat = new AddCat("CAT-2022-0105", "cookie", 3)

생성자 함수를 사용할 때에는 new키워드와 함께 함수를 호출해줍니다.

생성된 객체를 확인하면 입력한 데이터 프로퍼티를 가지고 있는 객체 인스턴스가 반환된 것을 확인할 수 있습니다.

그리고 [[Prototype]]에 우리가 등록한 upAge() 메서드를 확인할 수 있고, constructor에 어떤 생성자 함수로 생성했는지 확인이 되는 것을 볼 수 있습니다.

 

 

이전 객체 리터럴을 생성하는 함수에서 동일한 메소드가 계속 생겼습니다.

객체 리터럴과 다르게 생성자 함수의 prototype에 메서드를 등록하면 동일한 참조값을 가지고 있는지 확인해보겠습니다.

const newCat2 = new AddCat("CAT-2022-0106", "latte", 3)

생성자 함수를 이용해 새로 생성한 두개의 객체가 가진 upAge 함수는 동일한 참조값을 가지고 있는 것을 확인할 수 있습니다.

생성된 객체는 편하게 이용하실 수 있습니다! 

 

 

 

 




생성자 함수를 new 키워드 없이 생성하면 어떻게 될까요?

const cattest = AddCat("CAT-2022-0107", "cookie", 3)

생성자 함수가 일반 함수로 호출이 됩니다. 이 경우 함수 내부에서 암묵적인 반환도 일어나지 않아 결과물로 undefined가 반환됩니다.

일반 함수로 호출할 때의 this가 무엇인지 출력해보았습니다.

일반 함수로 호출될때의 this는 window로 출력이 됩니다.

윈도우가 3살이 되어있네요.ㅎㅎㅎ 너무 신기하죠? 네!

 

 

 

함수는 일반 객체와 다르게 호출할 수 있습니다. 자바스크립트에서는 함수로써 동작하기 위한 [[Environment]]와 [[FormalParameters]]등의 내부 슬롯과 [[Call]] [[Construct]]와 같은 내부 메서드를 추가로 갖고 있습니다.

일반 함수로 호출이 될 때에는 [[Call]] 이 호출이 되며 일반 함수가 되고

new 키워드를 붙혀 생성자 함수로  [[Construct]]가 호출이 되며 생성자 함수의 역할을 실행하게 되죠

 

 

 

 

 

 

 

모든 함수들에게 [[Call]][[Construct]]가 있을까요?

답은 아니요. constructor 함수와 non-constructor 함수가 있습니다. 자바스크립트 엔진은 함수 정의 방식에 따라 구분을 합니다. 

 

함수 선언문, 함수 표현식, 클래스를 사용하여 정의된 함수는  constructor 함수로, 

메서드의 축약표현으로 생성된 함수나 화살표 함수()=>{} 로 정의된 함수는 non-constructor로 구분됩니다. 메서드의 축약표현이나 화살표 함수로 정의된 경우 constructor로 사용이 불가능합니다. 

화살표 함수로 생성한 함수의 경우 new 키워드를 이용해 함수를 호출하면 TypeError가 반환됩니다.

 

 

 

 

 

생성자 함수 내부에서 생성자 함수로 호출되었는지 확인할 수 있을까요? 

네. ES6에서 new.target 이 추가되었습니다. 생성자 함수에서 new.target을 호출하면 어떻게 되는지 확인해보겠습니다.

 

생성자 함수 내에서 new.target을 호출하면 자기 자신이 반환되는 것을 확인할 수 있습니다.

 

 

일반 함수로 호출하게 되면? new.target은 undefined가 호출됩니다. 

 

 

new.target을 이용해서 생성자 함수가 일반 함수로 호출된 경우, 생성자 함수로 다시 리턴해주는 방법으로 사용할 수 있습니다.

function AddCat(code, name, age) {
    if(!new.target) {
        return new AddCat(code, name, age)
    }
	this.code = code;
	this.name = name;
	this.age = age;

  AddCat.prototype.upAge = function () {
    this.age +=1; 
  }
}

실수로 인해 new 연산자 없이 호출하게 되어도, 원하는 객체를 반환받을 수 있습니다.

생성자 함수가 무엇인지, 생성자 함수의 장점과 생성자 함수를 어떻게 사용할 수 있는지 방법을 함께 살펴보았습니다. 

 

 


생.성.자.함.수!!

사실 저번주 스터디인데, 처음 이해를 제대로 못하고  var,let,const에 굳이 열정을 불태웠습니다.. 하하하 (어려운 글을 잠시 미뤘달까요)

스터디 진행을 해주시는 hoon님께 생성자 함수가 new 없이 실행되는 원리와 new Object()안에 무엇을 넣어도 에러가 나지 않고 반환이 되는 이유를 듣다보니 머릿속에 정리가 되었습니다..!! ㅎㅎ 와 👍 감사합니다!!

 

생성자 함수를 배우며 함수선언문과 화살표함수의 차이도 알게 되었습니다. 화살표함수와 메서드는 non-constructor였다니 ㅎㅎ

그리고 객체와 함수의 차이점!! 동일한 객체임에도 함수 객체는 호출할 수 있는 이유가 내부 메서드 [[Call]]과 [[Construct]]의 여부라는 것도 알게 되었습니다. 

 

아!! 일반 함수를 new 연산자를 붙혀 호출하면 어떤 값이 리턴되는지도 확인해봐야겠네요. 

코딩 너무 재밌습니다 :) 

 

즐거운 코딩라이프되세요~~

JavaScript 스터디 팀 러버덕과 함께 합니다.

 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/06   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
글 보관함