new
연산자와 함께 Object
생성자 함수를 호출하면 빈 객체를 생성하여 반환
빈 객체를 생성한 이후 프로퍼티 또는 메서드를 추가하여 객체 완성
// 빈 객체 생성
const person = new Object();
// 프로퍼티 추가
person.name = 'Lee';
person.sayHello = function() {
console.log('Hi! My name is ' + this.name);
};
console.log(person); // {name: "Lee", sayHello: f}
person.sayHello(); // Hi! My name is Lee
new
연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수
생성자 함수에 의해 생성된 객체를 인스턴스라고 함
Object
생성자 함수 외에도String
, Number
, Boolean
, Function
, Array
, Date
, RegExp
, promise
등의 빌트인 생성자 함수 제공// String 생성자 함수에 의한 String 객체 생성
const strObj = new String('Lee');
console.log(typeof strObj); // object
console.log(strObj); // String {"Lee"}
// Number 생성자 함수에 의한 Number 객체 생성
const numObj = new Number(123);
console.log(typeof numObj); // object
console.log(numObj); // Number {123}
// Boolean 생성자 함수에 의한 Boolean 객체 생성
const boolObj = new Boolean(true);
console.log(typeof boolObj); // object
console.log(boolObj); // Boolean {true}
// Function 생성자 함수에 의한 Function 객체(함수) 생성
const func = new Function('x', 'return x * x');
console.log(typeof func); // function
console.dir(func); // f anonymous(x)
// Array 생성자 함수에 의한 Array 객체(배열) 생성
const arr = new Array(1, 2, 3);
console.log(typeof arr); // object
console.log(arr); // [1, 2, 3]
// RegExp 생성자 함수에 의한 RegExp(정규 표현식) 객체 생성
const regExp = new RegExp(/ab+c/i);
console.log(typeof regExp); // object
console.log(regExp); // /ab+c/i
// Date 생성자 함수에 의한 Date 객체 생성
const date = new Date();
console.log(typeof date); // object
console.log(date); // Thu Jul 07 2022 15:13:49 GMT+0900 (한국 표준시)
사실 객체 리터럴을 사용하는 것이 더 간편함❗️
객체 리터럴에 의한 객체 생성 방식은 직관적이고 간편하지만, 단 하나의 객체만 생성
동일한 프로퍼티를 갖는 객체를 여러 개 생성해야 하는 경우, 매번 같은 프로퍼티를 기술해야 함 (비효율적)
const circle1 = {
radius: 5,
getDiameter() {
return 2 * this.radius;
}
};
console.log(circle1.getDiameter()); // 10
const circle2 = {
radius: 10,
getDiameter() {
return 2 * this.radius;
}
};
console.log(circle2.getDiameter()); // 20
생성자 함수를 사용하여 프로퍼티 구조가 동일한 객체 여러 개를 간편하게 생성할 수 있음
일반함수와 동일한 방법으로 생성자 함수를 정의하고, new
연산자와 함께 호출하면 해당 함수는 생성자 함수로 동작
// 생성자 함수
function Circle(radius) {
this.radius = raduis;
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
new
연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수가 아닌 일반 함수로 동작const circle3 = Circle(15);
console.log(circle3); // undefined, 반환문이 없으므로 암묵적으로 undefined 반환
console.log(radius); // 15, 일반 함수로서 호출된 Circle 내의 this는 전역 객체를 가리킴
this
: 객체 자신의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수
this
가 가리키는 값은 함수 호출 방식에 따라 동적으로 결정됨
함수 호출 방식 this가 가리키는 값 일반 함수로서 호출 전역 객체 메서드로서 호출 메서드를 호출한 객체 생성자 함수로서 호출 생성자 함수가 생성할 인스턴스
// 생성자 함수
function Circle(radius) {
// 인스턴스 초기화
this.radius = radius;
this.getDiameter = function() {
return 2 * this.radius;
};
}
// 인스턴스 생성
const circle1 = new Circle(5);
new
연산자와 함께 생성자 함수를 호출하면 자바스크립트 엔진은 다음 과정을 거쳐 암묵적으로 인스턴스를 생성하고 초기화한 후 암묵적으로 인스턴스를 반환함암묵적으로 빈 객체 생성 (생성자 함수가 생성한 인스턴스)
암묵적으로 생성된 빈 객체(인스턴스)는 this에 바인딩
: 생성자 함수 내부의 this가 생성할 인스턴스를 가르키는 이유❗️
이 처리는 런타임 이전에 실행
생성자 함수에 기술되어 있는 코드가 한 줄씩 실행되어 this에 바인딩되어 있는 인스턴스 초기화
this에 바인딩되어 있는 인스턴스에 프로퍼티나 메서드를 추가하고 생성자 함수가 인수로 전달받은 초기값을 인스턴스 프로퍼티에 할당하여 초기화하거나 고정 값 할당
function Circle(radius) {
// 1. 암묵적으로 빈 객체(인스턴스) 생성, this에 바인딩
// 2. this에 바인딩되어 있는 인스턴스 초기화
this.radius = radius;
this.getDiameter = function() {
return 2 * radius;
};
// 3. 완성된 인스턴스가 바인딩된 this를 암묵적으로 반환
}
// 인스턴스 생성, Circle 생성자 함수는 암묵적으로 this를 반환
const circle = new Circle(1);
console.log(circle); // Circle {radius: 1, getDiameter: f}
function Circle(radius) {
this.radius = radius;
this.getDiameter = function() {
return 2 * radius;
};
return {};
}
const circle = new Circle(1);
console.log(circle); // {}
function Circle(radius) {
this.radius = radius;
this.getDiameter = function() {
return 2 * radius;
};
return 100;
}
const circle = new Circle(1);
console.log(circle); // Circle {radius: 1, getDiameter: f}
[[Environment]]
, [[FormalParameters]]
등의 내부 슬롯과 [[Call]]
, [[Construct]]
같은 내부 메서드를 추가적으로 가짐함수가 일반 함수로서 호출되면, 함수 객체의 내부 메서드 [[Call]]
이 호출
[[Call]]
을 갖는 함수 객체를 callablenew 연산자와 함께 생성자 함수로서 호출되면, 내부 메서드 [[Construct]]
호출
[[Construct]]
을 갖는 함수 객체를 constructor, 그렇지 않은 객체를 non-constructorfunction foo() {}
// 일반적인 함수로서 호출 : [[Call]] 호출
foo();
// 생성자 함수로서 호출 : [[Construct]] 호출
new foo();
callable : 호출할 수 있는 객체 (함수)
constructor : 생성자 함수로 호출할 수 있는 함수
non-constructor : 객체를 생성자 함수로서 호출할 수 없는 함수
→ 함수 객체는 callable이면서 constructor이거나 non-constructor
함수 정의를 평가하여 함수 객체를 생성할 때 함수 정의 방식에 따라 constructor와 non-constructor로 구분
constructor
함수 선언문, 함수 표현식, 클래스
non-constructor
메서드(ES6 메서드 축약 표현), 화살표 함수
// 일반 함수 정의 : 함수 선언문, 함수 표현식
function foo() {};
const bar = function () {};
const baz = {
x: function () {}
};
// 일반함수로 정의된 함수만 constructor
new foo();
new bar();
new baz.x();
// 화살표 함수 정의
const arrow = () => {};
new arrow(); // TypeError: arrow is not a constructor
// 메서드(ES6 메서드 축약 표현)
const obj = {
x() {}
};
new obj.x(); // TypeError: obj.x is not a constructor
일반 함수와 생성자 함수에 특별한 형식적 차이 X
new 연산자와 함께 함수를 호출하면 해당 함수는 생성자 함수로 동작
[[Construct]]
호출
→ 즉, new 연산자와 함께 호출하는 함수는 constructor여야 함
// 생성자 함수로서 정의하지 않은 일반 함수
function add(x, y) {
return x + y;
}
// 생성자 함수로서 정의하지 않은 일반 함수를 new 연산자와 함께 호출
let inst = new add();
console.log(inst); // {} : 함수가 객체를 반환하지 않았으므로 반환문이 무시
// 객체를 반환하는 일반 함수
function createUser(name, role) {
return { name, role };
}
// 일반함수를 new연산자와 함께 호출
inst = new createUser('Lee', 'admin');
console.log(inst); // {name: "Lee", role: "admin"} : 함수가 생성한 객체를 반환
new
연산자 없이 생성자 함수를 호출하면 일반 함수가 호출됨[[Call]]
메서드 호출됨function Circle(radius) {
this.radius = radius;
this.getDiameter = function() {
return 2 * this.radius;
};
}
const circle = Circle(5);
console.log(circle); // undefined
console.log(radius); // 5
console.log(getDiameter()); // 10
circle.getDiameter(); // TypeError: Cannot read property 'getDiameter' of undefined
생성자 함수가 new
연산자 없이 호출되는 것을 방지하기 위해 ES6에서는 new.target
지원
함수 내부에서 new.target
을 사용하면 new
연산자와 함께 생성자 함수로서 호출되었는지 확인할 수 있음
new.target
은 함수 자신을 가리킴new.target
은 undefinedfunction Circle(radius) {
if (!new.target){
// new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스 반환
return new Circle(radius);
}
this.radius = radius;
this.getDiameter = function() {
return 2 * radius;
};
}
// new 연산자 없이 생성자 함수를 호출하여도 new.target을 통해 생성자 함수로 호출
const circle = Circle(5);
console.log(circle.getDiameter()); // 10
※ new.target
은 IE에서 지원하지 않음 ❗️
→ 스코프 세이프 생성자 패턴
사용
function Circle(radius) {
if (!(this instanceof Circle)){
return new Circle(radius);
}
this.radius = radius;
this.getDiameter = function() {
return 2 * radius;
};
}
const circle = Circle(5);
new
연산자와 함께 호출되었는지 확인 후 적절한 값 반환new
연산자 없이 호출해도 new
연산자와 함께 호출했을 때와 동일하게 동작let obj = new Object();
console.log(obj);
obj = Object();
console.log(obj);
let f = new Function('x', 'return x * x');
console.log(f);
f = Function('x', 'return x * x');
console.log(f);
const str = String(123);
console.log(str, typeof str); // 123 string
const num = Number('123');
console.log(num, typeof num); // 123 Number
const bool = Boolean('true');
console.log(bool, typeof bool); // true Boolean