생성자 함수 == new 연산자와 함께 호출하여 객체를 생성하는 함수 (
String, Number, Boolean, Function, Array, Date, RegExp, Promise등) 인스턴스 == 생성자 함숭 의해 생성된 객체
- new 연산자와 함께 Object 생성자 함수를 호출하면 빈 객체를 생성하여 반환함.
- 빈 객체를 생성한 이후 프로퍼티, 메서드를 추가하여 객체를 완성함.
- 객체 리터럴에 의한 객체 생성 방식은 단 하나의 객체만 생성 가능
- "객체"는 "프로퍼티"를 통해 객체의 고유 상태를 표현함 "메소드"는 상태 데이터인 "프로퍼티"를 참조하고 조작하는 동작을 표현함
- 동일한 프로퍼티를 갖는 객체를 여러 개 생성할 경우 매번 같은 프로퍼티를 기술해야하므로 비효율적이다.
=> 즉,
circle1,circle2내getDiameter처럼 동일한 프로퍼티 구조임에도 따로 프로퍼티와 메서드를 기술해야함.
const circle1 = {
radius: 5,
getDiameter() {
return 2 * this.radius;
},
};
const circle2 = {
radius: 10,
getDiameter() {
return 2 * this.radius;
},
};- 객체(인스턴스)를 생성하기 위한 클래스처럼 프로퍼티 구조가 동일한 객체 여러 개를 생성할 수 있다.
- 생성자 함수를 new 연산자와 함께 호출 시, 해당 함수는 생정자 함수로 동작한다. new 연산자를 호출하지 않으면, 일반 함수로 동작한다.
// 생성자 함수
function Circle(radius) {
// 생성자 함수 내부의 this는 인스턴스를 가리킴
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
// 인스턴스 생성
const circle1 = new Circle(5);
const circle2 = new Circle(10);
console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20❓ this 객체 자신의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수로 this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다.
- 생성자 함수의 역할 == 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 클래스로 동작하여, 1) 인스턴스를 생성하기 2) 생성된 인스턴스를 초기화하기
function Circle(radius) {
// 인스턴스 초기화
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
// 인스턴스 생성
const circle1 = new Circle(5);=> this에 프로퍼티 추가 (확인) 전달된 인수를 프로퍼티 초기값으로서 할당하여 인스턴스 초기화(확인) 인스턴스를 생성하고 반환하는 건 자바스크립트 엔진이 암묵적인 처리를 통해 이뤄짐
암묵적으로 빈 인스턴스 객체가 생성됨 인스턴스는 this에 바인딩되어 생성자 함수 내부의 this가 생성자 함수가 생성할 인스턴스에 가리킴
❓ 바인딩 식별자와 값을 연결하는 과정 변수 선언은 변수 이름(식별자)과 확보된 메모리 공간의 주소를 바인딩하는 것
function Circle(radius) { // 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다. console.log(this); // Circle {} this.radius = radius; this.getDiameter = function () { return 2 * this.radius; }; }
생성자 함수에 기술되어있는 코드가 한 줄씩 실행되어 this에 바인딩 되어있는 인스턴스를 초기화함. 즉, this에 바인딩되어 있는 인스턴스에 프로퍼티나 메서드를 추가하고 생성자 함수가 인수로 전달받은 초기값을 인스턴스 프로퍼티에 할당하여 초기화하거나 고정값을 할당한다.
function Circle(radius) {
// 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
// 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}- 생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
function Circle(radius) {
// 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
// 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
// 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
}
// 인스턴스 생성. Circle 생성자 함수는 암묵적으로 this를 반환한다.
const circle = new Circle(1);
console.log(circle); // Circle {radius: 1, getDiameter: f}- 만약에 this가 아닌 다른 객체를 명시적으로 반환 시 return문에 명시한 객체가 반환된다.
function Circle(radius) {
// 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
// 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
// 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
// 명시적으로 객체를 반환하면 암묵적인 this 반환이 무시된다.
return {};
}
// 인스턴스 생성. Circle 생성자 함수는 명시적으로 반환된 객체를 반환한다.
const circle = new Circle(1);
console.log(circle); // {}- 명시적으로 원시 값을 반환하면 원시 값 반환은 무시되고 암묵적으로 this가 반환된다.
function Circle(radius) {
// 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
// 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
// 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
// 명시적으로 원시 값을 반환하면 원시 값 반홚은 무시되고 암묵적으로 this가 반환된다.
return 100;
}
// 인스턴스 생성. Circle 생성자 함수는 명시적으로 반환된 객체를 반환한다.
const circle = new Circle(1);
console.log(circle); // Circle {radius: 1, getDiameter: f}- 내부 메서드로 함수 호출과 객체 생성과 관련됨.
- 함수가 일반 함수로서 호출되면 함수 객체의 내부 메서드 **[[Call]]**이 호출됨. new 연산자와 함께 생성자 함수로서 호출되면 내부 메서드 **[[Construct]]**가 호출됨.
function foo() {}
// 일반적인 함수로서 호출: [[call]] 호출됨
foo();
// 생성자 함수로서 호출: [[construct]] 호출됨
new foo();
- constructor: 함수 선언문, 함수 표현식, 클래스
- non-constructor: 메서드(ES6), 화살표 함수
[[Call]]과 [[Construct]]는 ES6 이후에도 계속 사용됩니다. 다만, ES6 이후의 문법에서는 class와 arrow function이 도입되면서 동작 방식이 조금 달라졌습니다. 1. 클래스(Class)와 [[Construct]]
- class 키워드로 정의된 클래스는 반드시 new와 함께 호출해야 합니다.
- 일반 함수처럼 호출하면 TypeError가 발생합니다.
class Car {
constructor(model) {
this.model = model;
}
}
// new 없이 호출하면 에러 발생
// Car("Tesla"); // TypeError: Class constructor Car cannot be invoked without 'new'
const myCar = new Car("Tesla"); // [[Construct]]가 실행됨
console.log(myCar.model); // "Tesla"2. 화살표 함수(Arrow Function)와 [[Call]]
- 화살표 함수는 [[Call]]은 있지만 [[Construct]]가 없습니다. 즉, new 키워드로 호출할 수 없습니다.
const ArrowFunc = () => {};
ArrowFunc(); // 정상 실행 ([[Call]]이 실행됨)
// new ArrowFunc(); // TypeError: ArrowFunc is not a constructor- 일반 함수와 생성자 함수에 형식적인 차이가 없어, new 연산자와 함께 함수를 호출하면 해당 함수는 생성자 함수로 동작한다.
- 즉, 함수 객체 내부 메서드 [[Constructor]]가 호출된다.
- new 연산자와 함께 호출하는 함수는 constructor이어야 함.(new 연산자는 생성자 함수를 호출할 때 사용됨.)
1. new 연산자 없이 함수 호출 [[Call]] 실행됨
function showThis() {
console.log(this);
}
showThis(); // 일반 호출 (브라우저: window, Node.js: global)
==============================
"use strict";
function showThis() {
console.log(this);
}
showThis(); // undefined- 엄격 모드('use strict')가 아닐 때 → this는 window(브라우저) 또는 global(Node.js)
- 엄격 모드('use strict')일 때 → this는 undefined
2. new 연산자로 함수 호출 [[Construct]] 실행됨
function Person(name) {
this.name = name; // 새로운 객체의 프로퍼티가 됨
console.log(this); // 새로운 객체 출력
}
const p1 = new Person("Alice"); // new를 사용했으므로 새로운 객체 생성
console.log(p1.name); // "Alice"ES6 때 도입된 언어
- new 연산자와 함께 생성자 함수로서 호출되면, 함수 내부의 new.target은
함수 자신을 가리킴.- new 연산자 없이 일반 함수로서 호출된 함수 내부의 new.target은
undefined이다.
function Circle(radius) {
// new 연산자와 함께 호출되지 않으면, new.target == undefined
if (!new.target) {
// new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스를 반환함.
return new Circle(radius);
}
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
// new 연산자 없이 생성자 함수를 호출해도 new.target을 통해 생성자 함수로서 호출됨.
const circle = Circle(5);
console.log(circle.getDiameter());=> 대신 !(this instance of Circle)을 사용해도 됨