모던 자바스크립트 Deep Dive 정리
▶ 객체 생성방법 2가지
1. 객체 리터럴
2. 생성자 함수
Object 생성자 함수
▶ new 연산자와 함께 Object 생성자 함수를 호출하면 빈객체를 생성하여 반환한다
▷ 예시
// 빈 객체의 생성
const person = new Object();
▶ 인스턴스
▷ 생성자 함수에 의해 생성된 객체
생성자 함수
▶ 생성자 함수
▷ new 연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수
▶ 인스턴스
▷ 생성자 함수에 의해 생성된 객체
▶ Object 생성자 함수 이외에도 String, Number, Boolean, Function, Array 등의 빌트인 생성자 함수가 있음
객체 리터럴 방식 vs 생성자 함수 방식
객체 리터럴 방식
▶ 간편하긴 하나, 동일한 프로퍼티를 갖는 객체를 여러개 생성해야 하는 경우 비효율적
생성자 함수 방식
▶ 객체(인스턴스)를 생성하기 위한 템플릿(클래스)처럼 간편하게 여러 객체 생성 가능
▷ 예시
// 생성자 함수
function Circle(radius) {
// 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
this.radius = radius;
this.getDiameter = function () { // 클래스였다면 메서드는 이름만 써도 됨
return 2 * this.radius;
};
}
// 인스턴스의 생성
const circle1 = new Circle(5); // 반지름이 5인 Circle 객체를 생성
const circle2 = new Circle(10); // 반지름이 10인 Circle 객체를 생성
console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20
▶ 언뜻 보기에는 일반 함수와 다를 바가 없다
▷ 대문자로 시작한다는 점, 함수 내부에 this 가 있다는 점을 들어 생성자 함수임을 유추할 수 있다
▷ 일반함수와 동일한 방식으로 생성자 함수를 정의하고, new 연산자와 함께 호출하면 생성자 함수로 동작한다
▷ new 연산자 안쓰면 그냥 함수로 동작
this
▶ 객체 자신의 프로퍼티나 메서드를 참조하기 위한 자기참조변수
▷ this 바인딩(this가 가리키는 값) 은 함수 호출 방식에 따라 동적으로 결정된다
함수 호출 방식 | this가 가리키는 값(this 바인딩) |
일반 함수로서 호출 | 전역 객체 |
메서드로서 호출 | 메서드를 호출한 객체 |
생성자 함수로서 호출 | 생성자 함수가 (미래에) 생성할 인스턴스 |
전역 객체는
▷ 브라우저 환경에서는 window 객체
▷ Node.js 환경에서는 global 객체
생성자 함수의 인스턴스 생성 과정
1. 인스턴스 생성과 this 바인딩
1-1. 암묵적으로 빈 객체가 생성된다 (후에 인스턴스가 됨)
1-2. 인스턴스가 this에 바인딩됨 (생성자 함수 내부의 this)
▷ 이 과정들은 런타임 이전에 실행됨
▶ 바인딩
▷ 식별자와 값을 연결하는 과정
▷ this 바인딩은 this와 this가 가리킬 객체를 바인딩
2. 인스턴스 초기화
2-1. 생성자 함수 내부의 코드가 한줄씩 실행되며 this에 바인딩된 인스턴스를 초기화
▷ 인스턴스에 프로퍼티나 메서드가 추가됨
▷ 또는 생성자 함수가 인수로 받은 초기값을 인스턴스 프로퍼티에 할당하여 초기화
3. 인스턴스 반환
3-1. 생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환됨
▷ 만약 this가 아닌 다른 객체를 명시적으로 반환하면 이 다른 객체가 반환됨 (인스턴스 무쓸모 만들어버림)
▷ 하지만 명시적으로 원시값을 반환하면 원시값 반환은 무시되고 암묵적으로 this가 반환됨
★ 생성자 함수 내부에서 return문을 반드시 생략해야함
내부 메서드 [[Call]] 과 [[Construct]]
▶ 함수 선언문 또는 함수 표현식으로 정의한 함수는 일반적인 함수 + 생성자 함수로도 호출 가능
▶ 함수도 객체이므로 메서드와 프로퍼티를 가질 수 있음
▶ 또한 일반 객체와 달리 호출할 수 있기 때문에 [[Call]], [[Construct]] 같은 내부 메서드를 갖고 있음
▶ [[Call]]
▷ 이 내부 메서드를 갖고 있는 함수 객체를 callable (호출할 수 있는 객체) 이라고 함
▷ 함수가 일반 함수로 호출되면 함수 객체 내부의 [[Call]] 이 호출됨
▶ [[Construct]]
▷ 이 내부 메서드를 갖고 있는 함수 객체를 construct (생성자 함수로 호출할 수 있는 함수) 라고 함
▷ 함수가 new 연산자와 함께 생성자 함수로 호출되면 함수 객체 내부의 [[Construct]] 이 호출됨
▶ 모든 함수 객체는 Call을 갖고 있지만 모두 Construct를 갖고 있는 것은 아님
constructor vs non-constructor
▶ constructor : 함수 선언문, 함수 표현식, 클래스 (클래스도 함수)
▶ non-constructor : 메서드(ES6 메서드 축약 표현), 화살표 함수
new.target
▶ ES6에서 도입됨
▶ 생성자 함수가 new 연산자 없이 호출되는 것을 방지하기 위해 사용
▶ new 와 함께 호출되면 new.target은 함수 자신을 가리킴 (event.target과 비슷)
▶ 일반 함수로 호출되면 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());
▶ 대부분의 표준 빌트인 생성자 함수(String, Object, Function ...) 는 new 연산자가 없어도 결과물을 잘 반환
▷ Objcet() or Array() 앞에 new 안붙여도 객체 생성됨
▶ String, Number, Boolean 생성자 함수는 new 연산자 없이 호출하여 데이터 타입을 변환하는 용도로 자주 쓰임