[JS딥다이브] 8. 생성자 함수, 함수 객체

게코젤리·2023년 5월 15일

생성자 함수


1. 객체를 생성하는 방식

객체 리터럴에 의한 객체 생성 방식이 가장 일반적이고 간단한 객체 생성 방식이지만 이외에도 다양한 방법으로 객체를 생성할 수 있다. 그 중에 생성자 함수를 사용하여 객체를 생성하는 방식을 알아보자.
생성자 함수new 연산자와 함께 호출하여 객체를 생성하는 함수를 말한다. 여기서 생성자 함수에 의해 생성된ㅇ 객체를 인스턴스라고 한다.
자바스크립트에선 Object, String, Number, Boolean, Function 등의 빌트인 생성자 함수를 제공한다.

const strObj = new String('hong');
console.log(strObj); // String {'hong'} // 여기서 String은 문자열이 아니라 객체의 타입 정보를 나타내는 주석 같은 것.

2. 생성자 함수를 쓰는 이유

객체 리터럴에 의한 객체 생성 방식은 간편하지만 단 하나의 객체만 생성하기 때문에 동일한 프로퍼티의 객체를 여러개 생성해야 하는 경우 비효율적이다. 이 때 생성자 함수를 사용하면 프로퍼티 구조가 동일한 객체 여러개를 간편하게 생성할 수 있다.

function Circle(radius){
	this.radius = radius;
}
cons circle1 = new Circle(5);
cons circle2 = new Circle(10);

3. 생성자 함수의 인스턴스 생성 과정

생성자 함수의 역할

  • 템플릿에 맞는 인스턴스 생성 (필수)
  • 생성된 인스턴스를 초기화 (옵션)
  • 암묵적으로 인스턴스를 생성하고 반환
  1. 인스턴스 생성과 this 바인딩
    암묵적으로 빈 객체가 생성된 뒤, this에 바인딩

  2. 인스턴스 초기화
    생성자 함수에 기술된 코드가 한 줄씩 실행되면, this에 바인딩 되어있는 인스턴스를 초기화

  3. 인스턴스 반환
    생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환.만약 명시적으로 다른 객체를 return 하면 this의 암묵적 반환이 무시되고 return한 객체가 반환되기 때문에 생성자 함수 내부에서 return 객체를 사용하면 안된다.

4. constructor vs non-constructor

함수는 객체지만 일반 객체와 다른 특징을 가진다. 일반 객체는 호출할 수 없지만 함수는 호출 가능하다는 점. 함수 객체는 일반 객체의 내부 슬롯, 내부 메서드를 포함하고 함수 객체만을 위한 [[Environment]], [[FormalParameters]]등의 내부 슬롯과 [[Call]], [[Construct]]과 같은 내부 메서드를 갖고 있다. 함수가 일반 함수로서 호출되면 [[Call]]이 호출되고 생성자 함수로 호출되면 [[Construct]]가 호출된다.

다만 모든 함수 객체는 호출할 수 있지만([[Call]]), 모든 함수 객체를 생성자 함수로 호출할 수는 없다. ([[Construct]]를 갖지 않음)

이 때, [[construct]] 호출의 경우 this는 생성될 객체가 되고, [[Call]] 호출의 경우 this는 전역 객체(window)가 된다.

// constructor 함수(함수 선언문, 함수 표현식)
function foo(){}
const bar = function(){}
const baz = {
  // x의 값으로 할당된 것은 메서드가 아니라 일반 함수로 정의된 함수
	x : function(){}
}

// non-constructor 함수(화살표 함수, 메서드)
const arrow = () => {};
const obj = {
	x(){}
}
new arrow(); // TypeError: arrow is not a constructor
new obj.x(); // TypeError: obj.x is not a constructor

함수 객체


## 1. 일급 객체 다음의 조건을 만족하는 객체를 일급 객체라고 한다.
  1. 무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다.
  2. 변수나 자료구조에 저장할 수 있다.
  3. 함수의 매개변수에 전달할 수 있다.
  4. 함수의 반환값으로 사용할 수 있다.

함수는 위 조건을 모두 만족하므로 일급 객체다. 다만, 일반 객체와 다르게 함수 객체는 호출할 수 있고 함수 고유의 프로퍼티를 갖고 있다.

2. 함수 객체의 프로퍼티

console.dir, Object.getOwnPropertyDescriptors()로 함수 객체의 프로퍼티를 확인해볼 수 있다.

2-1. arguments 프로퍼티

arguments arguments 값은 arguments 객체다. arguments 객체는 인수들의 정보를 담고 있는 iterable한 유사 배열 객체다. arguments 프로퍼티는 ES3부터 표준에서 폐지되었다. Function.arguments 같은 방식은 권장되지 않으며 함수 내부에서 지역 변수처럼 arguments 객체를 참조하는 방식으로 사용한다.

유사 배열 객체는 배열이 아니기 때문에 배열 메서드를 사용할 경우 에러가 생긴다.

function sum(){
  	// 아래와 같이 유사배열 arguments를 배열로 바꿔줘야 한다.
	const array = Array.prototype.slice.call(arguments);
  return array.reduce((a,b) => a + b);
}

위와 같은 번거로움을 해결하기 위해 ES6에선 Rest 파라미터가 도입되었다.

2-2. __proto__ 프로퍼티

모든 객체는 [[Prototype]]이라는 내부 슬롯을 가지며, 이것은 객체지향 프로그래밍의 상속을 구현하는 프로토타입 객체를 가리킨다. __proto__ 프로퍼티는 [[Prototype]] 내부 슬롯이 가리키는 프로토타입 객체에 간접적으로 접근하기 위해 사용하는 접근자 프로퍼티다.

const test1 = { a : 1}
const test2 = () => 3;

console.log(test1.__proto__);// {constructor: ƒ, __defineGetter__: ƒ,…}
// test1 객체의 prototype은 기본 객체
// 기본 객체는 자바스크립트에서 모든 기본적인 객체가 공유하는 기능과 정보를 가지고 있다.
console.log(test2.__proto__);// ƒ () { [native code] }
// test2 객체의 prototype은 기본 함수 객체
// ƒ () { [native code] }는 기본 함수 객체가 자바스크립트 엔진 내부에서 정의된 함수라는 뜻

2-3. prototype 프로퍼티

prototype 프로퍼티는 생성자 함수가 생성할 인ㅌ스턴스의 프로토타입 객체를 가리킨다.

생성자 함수로만 호출할 수 있는 함수 객체가 갖는 프로퍼티. non-constructor에는 prototype 프로퍼티가 없다.(생성자 함수는 새로운 '시조'(프로토타입 객체)를 창조하는 것으로 볼 수 있다. 그리고 이 '시조'로부터 뻗어나온 각각의 객체(인스턴스)는 '시조'의 자손이다.)

(function(){}).hasOwnProperty('prototype'); // true
({}).hasOwnProperty('prototype'); // false

2-4. caller 프로퍼티

함수 객체의 caller 프로퍼티는 함수 자신을 호출한 함수를 가리킨다.

2-5. length 프로퍼티

함수를 정의할 때 선언한 매개변수의 개수를 가리킨다.

2-6. name 프로퍼티

함수 이름을 나타낸다.


📖 모던자바스크립트 딥다이브 17장 생성자 함수에 의한 객체 생성(234p)
📖 모던자바스크립트 딥다이브 18장 함수와 일급 객체(249p)

0개의 댓글