과거의 내가 미래의 나에게
딥다이브 :: 생성자 함수에 의한 객체 생성 본문
「딥다이브 자바스크립트」 정리 - 생성자 함수에 의한 객체 생성
※ 아래 내용은 책을 통해 학습한 것을 개인적으로 정리한 것으로 내용이 다소 부정확 할 수 있습니다.
생성자 함수
Object, String ,Number, Boolean, Function, Array, Date, RegExp 등.
생성자 함수는 new를 통해서 호출하는데 만약 new 없이 호출한다면 그냥 일반 함수로 작동한다.
< Object 생성자 함수 >
new Object()로 객체를 생성하고 그 이후 프로퍼티 또는 메서드를 추가하여 완성시킬 수 있다.하지만 객체 리터럴이 훨씬 더 간단하기에 굳이 사용할 필요는 없을듯…
생성자 함수를 통한 객체 생성
생성자 함수는?
함수 선언문 또는 함수 표현식으로 정의한 함수는 일반적인 함수로 호출하는 것은 물론 생성자 함수로서 호출할 수 있다. 일반 함수는 그냥 호출하면 되고, 생성자 함수로서 호출하려면 new 연산자와 함께 호출하면 된다.
생성자 함수는 프로퍼티 구조가 동일한 객체를 여러개 생성할 때 간편하게 할 수 있다. 객체 리터럴로 사용한다면 객체 자체를 생성시키기는 쉽지만 단 하나의 객체만 생성하기에 동일한 프로퍼티 구조를 가진 객체를 여러개 생성한다면 비효율적일 수도 있다.
생성자 함수의 역할은 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 템플릿으로 동작하여 인스턴스를 생성하는 것과 생성된 인스턴스를 초기화하는 것이다.
생성자 함수가 생성되는 과정?
function Circle(radius){
console.log(this) //Circle {} -> 1. 인스터스에 this가 바인딩 되어있음
//2. 인스턴스 초기화
this.radius = radius;
this.getDiameter = function(){
return 2 * this.radius
}
//3. 완성된 인스턴스가 바인딩된 this 암묵적 반환
}
const circle1 = new Circle(5)
circle1.getDiameter() // 10
생성자 함수가 인스턴스를 생성하고 인스턴스를 초기화한 후 반환하는 과정은 아래와 같다.
- 인스턴스 생성과 this 바인딩
new 함수명으로 호출 시, 먼저 암묵적으로 빈 객체(인스턴스임)가 생성된다. 그리고 빈 객체는 this에 바인딩한다. 그리고 나서 함수의 런타임이 시작된다. - 인스턴스 초기화
this에 바인딩되어있는 빈 객체에 프로퍼티나 메서드를 추가하고 생성자 함수가 인수로 전달 받은 초기값을 인스턴스 프로퍼티에 할당하여 초기화하거나 고정값을 할당한다. 이는 개발자들이 작업하는 부분이다.
만약 생성자 함수로 호출하지 않고 일반 함수로 호출한다면 함수 몸체 내 this는 바인딩 되지 않았기에 전역객체를 가리키게 된다 - 인스턴스 반환
함수 내부에 모든 처리 끝나면 완성된 인스턴스가 바인딩된 this는 암묵적으로 반환된다.
만약 생성자 함수 내에 return {}을 반환하면 this 반환은 무시되고, return 원시값을 반환하면 return 반환이 무시되고 this가 반환된다. 어찌되었든 생성자 함수로써 활용하려면 명시적으로 return을 쓰면 안될 것이다.
함수 객체와 일반 객체의 다른 점?
함수 객체는 일반 객체가 가지고 있는 내부 슬롯과 내부 메서드를 모두 가지고 있다. 함수도 어쨌든 객체이므로 일반 객체와 동일하게 동작한다.
function test (){}
test.prop = 10
test.methhod = function() {/...}
console.log(test.prop) //10
함수 객체는 함수 객체만의 내부 슬롯([[Environment]],[[FormalParameters]])과 내부 메서드([[call]],[[Construct]])가 존재한다. 이것이 함수가 일반 객체와 다른 점이고, 이러한 함수 객체만의 내부슬롯, 내부메서드가 존재하기에 함수는 호출이 가능하단 점이다.
함수의 호출
함수가 일반 함수로 호출되면 call이 호출되고, 생성자 함수로 호출되면 construct가 호출된다. 일반 함수와 생성자 함수의 특별한 형식적 차이는 없으므로 생성자 함수를 파스칼 케이스로 명명하여 일반 함수와 구별할 수 있도록 노력해야한다.
모든 함수 객체는 call을 가지고 있다. 호출할 수 없는 객체는 함수 객체가 아니기 때문이다.
하지만 모든 함수가 construct를 갖는 것은 아니다. 정리하면 모든 함수는 호출 할 수 있지만 모든 함수가 생성자 함수로써 작동할 수 있는 것은 아니라는 것이다.
constructor과 non-constructor
constructor과 non-constructor의 구분은 함수 정의 방식에 따라 달라진다.
- constructor: 함수 선언문, 함수 표현식, 클래스
- non-constructor: 메서드(ES6의 메서드 축약 표현만 메서드로 인정한다. 함수를 프로퍼티 값으로 사용하면 일반적으로 메서드로 통칭하지만 ECMA에서는 ES6의 메서드 축약 표현만을 메서드로 의미한다), 화살표 함수
function test1 () {} //constructor
const test2 = function () {}// constructor
const test3 = {x:function () {}} //non-constructor
new 연산자
new 연산자는 construct 함수와 함께 호출되어야 한다. 생성자 함수는 new 연산자없이는 부르지 않도록 주의해아한다.
일반 함수와 생성자 함수 호출이 형식적으로 비슷하기에 파스칼케이스로 구분해놓기도 하지만 어쨌든 new없이 생성자 함수를 언제든 부를 수는 있다. 이를 방지하기 위해 new.target을 사용한다.
new.target
함수 내부에서 new.target을 사용하면 생성자 함수로 호출되었는지 아닌지 확인할 수 있다. new 연산자 없이 호출되면 이는 undefined가 된다.
function Circle(radius) { // 해당 함수는 생성자 함수로써 사용하기 위해 만들어놓음
if(!new.target){ // new 연산자를 사용하지 않으면 일반 함수로써 호출되니 그걸 방지하기 위해 씀
return new Circle(radius) // new를 붙여 재귀적으로 다시 생성자 함수로 호출
}
}
스코프 세이프 생성자 패턴
new.target은 IE에서 제공되지 않으므로, 이를 사용할 수 없는 환경이면 스코프 세이프 생성자 패턴을 사용할 수 있다.
생성자 함수로써 호출되면 먼저 인스턴스를 생성하고 this를 바인딩하는데, 이 때 this와 인스턴스는 프로토타입에 의해 연결된다.
만약 일반 함수로 호출되면 this는 인스턴스와 연결되지 않았을테니 이는 전역 객체를 가리킬 것이다. 이 차이를 이용한다.
function Circle(radius) {
if(!(this instanceof Circle)){
return new Circle(radius)
}
}
'js 이론' 카테고리의 다른 글
구조 분해 할당 구문 (0) | 2023.08.31 |
---|---|
js 시간 (0) | 2023.08.21 |
딥다이브 :: property attribute (0) | 2023.02.01 |
딥다이브 :: let, const (0) | 2023.01.24 |
딥다이브 :: 스코프 (0) | 2023.01.24 |