💡 17.1 Object 생성자 함수
- 생성자 함수(constructor)
- new 연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수
- 빈 객체 생성하여 반환 ⇒ 프로퍼티/메서드 추가
- new Object() new String() new Number() new Date() (빌트인 생성자 함수 제공)
const person = new Object();
person.name = 'Lee';
person.sayHello = function () {
console.log('Hi! My name is ' + this.name);
};
console.log(person); // {name: "Lee", sayHello: ƒ}
person.sayHello(); // Hi! My name is Lee
new Object() 보다는 객체 리터럴{}이 더 간편. 무조건 생성자 함수를 써야 하는 게 X
그러나, 객체 리터럴{}의 경우 수십 개의 객체를 생성해야 한다면? 비효율적!
💡 17.2 생성자 함수
✨ 생성자 함수 → 인스턴스 생성 방식
- this : 자신의 프로퍼티/메서드를 참조하기 위한 자기 참조 변수
function Circle(radius) {
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
const circle1 = new Circle(5);
console.log(circle1.getDiameter()); // 10
- 만약 new 연산자가 없다면? 일반 함수로서 호출!
function Circle(radius) {
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
console.log(Circle(5)); // undefined 반환! 현재 Circle에 return이 없으므로!
✨ 생성자 함수 → 인스턴스 생성 과정
- 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
- this에 바인딩되어 있는 인스턴스를 초기화한다. (by 개발자)
- 암묵적으로 this를 반환한다.
function Circle(radius) {
// 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
console.log(this); // Circle {}
// 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
// 3. 암묵적으로 this를 반환한다.
}
// 인스턴스 생성. Circle 생성자 함수는 명시적으로 반환한 객체를 반환한다.
const circle = new Circle(1);
console.log(circle); // Circle {radius: 1, getDiameter: ƒ}
- 만약, 객체를 명시적으로 반환할 경우 반환은 됨.
// 생략
return {};
}
console.log(new Circle(1)); // {}
- 그러나, 생성자 함수에서 원시값을 명시적으로 반환할 경우, 이는 무시되고 this가 반환된다!
// 생략
return 1;
}
console.log(new Circle(1)); // Circle {radius: 1, getDiameter: ƒ}
✨ 내부 메서드 [[Call]] [[Construct]]
- 함수는 객체로써, 프로퍼티, 메서드를 가질 수 있다.
function foo() {}
foo.prop = 10;
foo.method = function () {
console.log(this.prop);
};
- 그러나 일반객체와는 다르게 함수는 호출할 수 있다!
- 함수 호출 → 함수 객체의 내부 메서드 [[Call]] 호출
- callable: 내부 메서드 [[Call]] 을 갖는 함수 객체
- 생성자 함수(new 연산자) 호출 → 내부 메서드 [[Construct]] 호출
- constructor: 내부 메서드 [[Construct]] 을 갖는 함수 객체 ↔ non-constructor
- 함수 호출 → 함수 객체의 내부 메서드 [[Call]] 호출
function foo() {}
// 일반적인 함수로서 호출: [[Call]]이 호출된다.
foo();
// 생성자 함수로서 호출: [[Construct]]가 호출된다.
new foo();
⇒ 함수 객체는 호출되어야 하므로, callable이어야 하지만, constructor는 일수도, 아닐수도!
✨ constructor, non-constructor 구분
- 함수 정의 방식에 따라 구분됨
- constructor
- 함수 선언문, 함수 표현식, 클래스(클래스도 함수)
- non-constructor (new 생성자로 호출 X)
- 메서드(ES6 메서드 축약 표현), 화살표 함수
🚨 주의 : 메서드로 인정하는 범위가 일반적인 의미보다 좁다!
function foo() {} // => constructor
const bar = function () {}; // => constructor
const arrow = () => {}; // 화살표 함수 => non-constructor
const obj = {
print1: function() { // 메서드 X, 일반 함수로 정의 됨 => constructor
console.log('1');
},
print2() { // 메서드 축약 표현. 이것만 메서드로 인정함 => non-constructor
console.log('2');
}
};
new foo(); // -> foo {}
new bar(); // -> bar {}
new obj.print1(); // -> x {}
new arrow(); // TypeError: arrow is not a constructor
new obj.print2(); // TypeError: obj.print2 is not a constructor
✨ new 연산자
- 생성자 함수(new 연산자)와, 일반 함수의 차이점은 무엇인가!
- [[Call]] 을 호출하는가 [[Construct]] 을 호출하는가의 차이
- 즉, 생성자 함수로 호출되려면, non-constructor 가 아닌 constructor 여야 한다.
- 그렇기에, 생성자 함수는 파스칼케이스로 명명하여 일반 함수와 구분 짓자!
function add(x, y) {
return x + y;
}
let inst = new add();
console.log(inst); // add {}
// 함수는 원시값을 반환하지, 객체를 반환하지 않음 => 이에 this인 add {}가 반환됨!
function createUser(name, role) {
return { name, role };
}
inst = new createUser('Lee', 'admin');
console.log(inst); // {name: "Lee", role: "admin"}
// 바로 return하는 객체를 반환함! 원시값이 아닌 객체를 return 해줬으니까!
- 일반 함수로 호출할 때, this는 어떻게 되는가? 원래는 생성할 인스턴스를 가리키는데..
- 전역 객체 window를 가리킨다!!!
function Circle(radius) {
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
// this는 전역 객체 window를 가리키고
// radius, getDiameter 메서드는 전역객체의 프로퍼티와 메서드가 된다!!!
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.target
- 생성자 함수가 new 없이 호출되는 것을 방지하기 위해 사용
- 생성자 함수는 파스칼 케이스 컨벤션이 있지만, 실수할 수 있기 때문!
- this와 비슷하게 암묵적인 지역변수처럼 사용됨. 💡메타 프로퍼티!
- 생성자 함수로써 호출되면, new.target 은 함수 자신을 가리키지만
- 일반 함수로써 호출되면, new.target 은 undefined!
- new 생성자 함수로 호출하지 않아도, 내부에서 할당해 주는 코드
function Circle(radius) {
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());
- 그러나 ES6에서 도입된 최신문법으로 IE에선 지원 X
- 스코프 세이프 생성자 패턴 instanceof 사용!
function Circle(radius) {
if (!(this instanceof Circle)) {
return new Circle(radius);
}
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
const circle = Circle(5);
console.log(circle.getDiameter()); // 10
- 빌트인 생성자 함수는 new 연산자로 호출되었는지 확인한 후, 적절한 값 반환
- Object, Function는 new연산자 없이 호출해도 new연산자 생성과 동일하게 동작
- 반면, String, Number, Boolean는 new연산자 유무에 따라 객체/원시값 반환!!
const num = Number('123');
console.log(num, typeof num); // 123 number
console.log(new Number('123')) // Number {123}
🎈 내용 정리
생성자 함수 vs 일반 함수
Circle 함수
function Circle(radius) {
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
- 함수는 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
- this에 바인딩되어있는 인스턴스를 초기화한다
- 암묵적으로 this를 반환한다.
- new 연산자 O => 인스턴스 생성
- const circle = new Circle(1); // this는 객체 자신
- new 연산자 X => 일반 함수로서 호출
- const circle = Circle(1) // this는 전역 객체 window
이 함수는 생성자 함수로 인스턴스 생성 용도로만 사용해 주세요
1. new.target
=> new 연산자 실수로 안쓸 수도 있으니 그냥 안에서 인스턴스 생성해서 반환해 버릴게
function Circle(radius) {
if (!new.target) {
// new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스를 반환한다.
return new Circle(radius);
}
...
2. 스코프 세이프 생성자 패턴 instanceof
=> new 연산자 실수로 안쓸 수도 있으니 그냥 안에서 인스턴스 생성해서 반환해 버릴게 2
function Circle(radius) {
if (!(this instanceof Circle)) {
return new Circle(radius);
}
...
반응형
'📝 Javascript > 모던 자바스크립트 Deep Dive' 카테고리의 다른 글
[Javascript] 모던 자바스크립트 Deep Dive - 20장 strict mode (0) | 2024.01.08 |
---|---|
[Javascript] 모던 자바스크립트 Deep Dive - 18장 함수와 일급 객체 (1) | 2024.01.06 |
[Javascript] 모던 자바스크립트 Deep Dive - 16장 프로퍼티 어트리뷰트 (0) | 2023.09.09 |
[Javascript] 모던 자바스크립트 Deep Dive - 15장 let, const 키워드와 블록 레벨 스코프 (0) | 2023.08.20 |
[Javascript] 모던 자바스크립트 Deep Dive - 14장 전역 변수의 문제 (0) | 2023.08.16 |