1. 생성자 함수란?
- new 연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수
- 생성자 함수에 의해 생성된 객체를 인스턴스라고 한다.
- Object, String, Number, Boolean, Function, Array, Date, RegExp, Promise 등의 빌트인 생성자 함수 제공함
1-1. Object 생성자 함수
- Object 함수를 호출하면 빈 객체를 생성하여 반환
- 빈 객체를 생성한 이후 프로퍼티 또는 메서드를 추가하여 객체를 완성할 수 있다.
// object 로 빈 객체를 생성함
const person = new Object();
//빈 객체에 프로퍼티를 추가함
person.name = "hihilong";
person.sayHello = function () {
console.log ("hi. mmy name is" + this.name);
};
// Array, Date 같은 것들도 만들 수 있음
const date = new Date();
console.log(type of date);
// object
📌 이때 반드시 Object 생성자 함수를 통해 빈 객체를 생성해야하는 것은 아니다.
차라리 객체 리터럴을 사용하는 것이 더 나음...
https://khw063011.tistory.com/99
1-2. 객체 리터럴에 의한 객체 생성 방식의 문제점
- 객체 리터럴에 의한 객체 생성 방식은 단 하나의 객체만 생성한다.
- 따라서 동일한 프로퍼티를 갖는 객체를 여러 개 생성해야 하는 경우엔 매번 같은 프로퍼티를 기술해야하기 때문에 비효율적임...
- 프로퍼티 구조가 동일함에도 불구하고 매번 같은 프로퍼티와 메서드를 기술해야하는 불편함이 있다. . .
const circle1 = {
radius: 5;
getDiameters() {
return 2 * this.radius;
}
};
const circle2 = {
radius: 10;
getDiameters() {
return 2 * this.radius;
}
};
circle1 과 circle2 는 동일한 프로퍼티 구조를 가지고 있지만 서로 다른 객체이기 때문에
두번 작성해줘야 하는 불편함이 있다. . .
1-3. 생성자 함수에 의한 객체 생성 방식의 장점!
- 생성자 함수로 만들면.. 마치 객체를 생성하기 위한 템플릿처럼 생성자 함수를 사용하여 프로퍼티 구조가 동일한 객체 여러개를 간편하게 생성할 수 있다. !
- 또한 생성자 함수는 이름 그대로 객체를 생성하는데 일반 함수와 동일한 방법으로 생성자 함수를 정의하고 new 연산자와 함께 호출하면 해당 함수는 생성자 함수로 동작한다.
- 만약 New 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수가 아니라 일반 함수로 동작하게 된다.
// 생성자 함수 사용
function Circle(radius) {
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
// 여기서 this 는 생성자 함수가 생성할 인스턴스를 가지게 된다.
// 즉...this 키워드는 각 인스턴스 내부에서 해당 인스턴스를 참조하게 된다.
// 또한 여기서는.. radius 라는 프로퍼티와
// getDiameter 라는 메서드를 가지게 되는 것이다.
// 인스턴스 생성
const circle1 = new Circle(5);
const circle2 = new Circle(10);
this 에 대해 좀 더 알아보자!
this 는 객체 자신의 프로퍼티나 메서드를 참조하기 위한 자기참조변수이다!
this 가 가리키는 값, 즉 this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다.
1-4. 생성자 함수의 인스턴스 생성 과정
생성자 함수의 역할에 대해서 생각해보자.
생성자 함수의 역할은 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 템플렛으로 동작하여
인스턴스를 생성하는 것과 생성된 인스턴스를 초기화 하는 것이다.
생성자 함수가 인스턴스를 생성하는 것은 필수이고.. 생성된 인스턴스를 초기화하는 것은 옵션이다!
// 생성자 함수
function Circle(radius) {
//인스턴스 초기화
this.radius = radius;
this.getDiameter = function () {
return 2* this.radius;
}
};
//인스턴스 생성
const circle1= new Circle(5);
this 에 프로퍼티 추가 -> 필요에 따라 전달된 인수를 프로퍼티의 초기값으로 할당하여 인스턴스 초기화
하지만..반환하는 코드가 보이지 않는다 ?
JS 엔진은 암묵적인 처리를 통해 인스턴스를 생성하고 반환한다..
new 연산자와 함께 생성자 함수를 호출하면 암묵적으로 인스턴스를 생성하고 초기화한후 암묵적으로 인스턴스를 반환한다.
// 암묵적으로 빈 객체가 생성되고 this 에 바인딩
function Circle(radius) {
// this 에 바인딩되어있는 인스턴스를 초기화
this.radius = radius;
this.getDiameter = function() {
returnh 2* this.radius;
};
// 암묵적으로 this 를 반환한다.
return 100;
//만약 이것처럼 원시값을 반환하면 원시값은 무시되고 암묵적우로 this 반환된다.
}
즉 만약에 생성자 함수 내부에서 명시적으로 this 가 아닌 다른 값을 반환하면 생성자 함수의 기본 동작을 훼손하는 것이다. .
따라서 생성자 함수 내부에서 return 문을 반드시 생략해야한다.
2. 내부메서드 [[Call]] 과 [[Constructr ]]
- 함수는 객체라서 일반 객체와 동일하게 동작할 수 있지만 일반 객체와 다른 점이 있다.
- ⏯️ 일반 객체는 호출할 수 없지만 함수는 호출할 수 있다.
- 함수가 일반 함수로서 호출되면 함수 객체의 내부 메서드 [[Call]] 이 호출되고
- new 연산자와 함께 생성자 함수로서 호출되면 내부 메서드 [[Constructor]] 가 호출된다.
- 내부 메서드 [[Call]] 를 갖는 함수 객체를 callable 라고 하며 내부 메서드 [[Construct]] 를 가지는 함수 객체를 constructor, [[construct]] 를 가지지 않는 함수 객체를 non-constructor 라고 한다.
=> 모든 함수 객체는 callable 이지만 모든 함수 객체가 constructor 인 것은 아니다.
=> 즉 모든 함수 객체는 호출할 수 있지만 모든 함수 객체를 생성자 함수로서 호출할 수 있는 것은 아니다.
왜냐면...모든 함수 객체는 내부 메서드 [[Call]] 을 가지고 있는다.
따라서 호출을 할 수 있다.
하지만 ! 모든 함수 객체가 [[Construct]] 를 가지는 것은 아니다. .
따라서 함수 객체는 constructor 일수도 잇고 non-construct 일수도 있다.
따라서 무조건 callable 인것은 맞지만...
내부 메서드를 가지지 않을수도 있
기 때문에 생성자 함수로서 호출이 될수도 있고 안될수도 있다는 것이다.
🤔잠깐 타임 !
생성자 함수와 일반 함수라는게 멍미? !
생성자 함수는 new 로 호출되어 새로운 객체를 생성하고,
일반 함수는 단순히 호출되어 특정 작업(메서드) 을 수행한다.
2-1 constructor 와 non-constructor 의 구분
그렇다면 어떻게 자바스크립트 엔진이 둘을 구분하는지 생각해보자!
둘은 함수 객체를 생성할 때 함수 정의 방식에 따라 함수를 constructor 와 non-constructor 로 구분한다.
- constructor: 함수 선언문, 함수 표현식, 클래스
- non-constructor: 메서드, 화살표 함수
일반 함수를 정의할 때는 함수 선언문과 함수 표현식으로 정의를 내릴 수 있다.
즉... 일반 함수로 정의된 함수만이 constructor 이다.
2-2. new 연산자
- new 연산자와 함께 함수를 호출하면 해당 함수는 생성자 함수로 동작한다.
- 즉 다시 말해서... 함수를 호출하면 해당 함수는 생성자 함수로 동작하게 된다.
- 그런데 이때 new 연산자와 함께 호출하는 함수는 non-nconstructor 가 아니라 constructor 이여야 한다.
- 반대로 new 연산자 없이 생성자 함수를 호출하면 일반 함수로 호출이 된다.
- 다시 말해서 new 연산자 없이 호출을 하면 ! 함수 객체의 내부 메서드가 호출되는 것이 아니라 [[Call]] 이 호출된다.
2-3. new.target
=> 함수 내부에서 New.target 을 사용하면 new 연산자와 함께 생성자 함수로서 호출이 되었는가 확인할 수 있다.
=> new 연산자와 함께 생성자 함수로서 호출이 되면 함수 내부의 new. target 은 함수 자신을 가리킨다.
=> new 연산자 없이 일반 함수로서 호출된 함수 내부의 ㅜew.target은 undefined이다.
📌 Object, function 생성자 함수는 new 연산자 없이 호출해도 new 연산자와 함께 호출했을 때와 동일하게 동작한다.
📌 하지만 ... String, Boolean 등의 빌트인 생성자 함수들은 new 연산자 없이 호출하면 문자열, 숫자, 불리언 값을 반환한다.
📌 즉... 빌트인 생성자 함수들은 new 연산자와 함께 호출이 되었는지를 확인하고 적절한 값을 반환하게 된다.
정리를 해보자!
new 연산자를 사용하면:
- 새로운 객체(인스턴스) 가 생성된다.
- 생성자 함수 내부의 this 새로 생성된 객체를 참조한다.
- 생성자 함수가 반환하는 값이 명시되지 않으면, 자동으로 생성된 객체가 반환된다.
- (이때 return + 객체를 따로 지정해주면 this 가 반환되지 못함.. return + 원시값이 명시되면 무시됨)
- => 따라서 생성자 함수 내부에서 return 반드시 생략해줘야함..
new 연산자를 사용하지 않으면:
- 함수는 일반 함수로 호출
- this는 전역 객체를 참조한다.
- 함수의 반환 값은 명시적으로 정의된 return 값 (명시적 반환이 없으면 undefined 반환).
📌 결론: new 연산자를 사용하면 생성자 함수가 객체를 생성하고 this를 해당 객체로 바인딩하지만,
사용하지 않으면 함수는 일반 함수처럼 동작한다잉
'Deep Dive 정리' 카테고리의 다른 글
[JS Deep Dive] 18장 - 함수와 일급객체 (0) | 2024.09.04 |
---|---|
[JS] this 조금 더 정리하기 (0) | 2024.09.04 |
[JS Deep Dive] 16장 - 프로퍼티 어트리뷰트 (0) | 2024.09.01 |
[JS Deep Dive] 15장 - let const 키워드와 블록레벨 스코프 (0) | 2024.08.28 |
[JS Deep Dive] 13장 - 스코프 (0) | 2024.08.28 |