생성자 함수 호출
JS에서 생성자함수는 객체를 생성하는 역할을 함.
기존함수에 new 연산자를 붙이면 해당함수는 생성자 함수로 동작함.
여기서 잠깐~!
new연산자란 ?
"개발을 하다보면 유사한 객체를 여러개 만들어야할 상황이 온다.
new연산자와 생성자 함수를 사용하면 유사한 객체를 여러개 만들수 있다."라고 모질라 센세가 말했다.
{...}과 같은 객체 리터럴 문법을 사용해도 무방하지만, 일일이 객체를 만드는 것 보다 훨씬 간단하고 읽기 쉽게 객체를 만들 수 있다.
new연산자의 의의는 여기에 있다. 재사용할 수 있는 객체 생성 코드를 구현하는 것.
맨위에서 기존함수에 new 연산자를 붙이면 해당함수는 생성자 함수로 동작한다고 했는데,
반대로 생각해서 일반함수에 new연산자를 붙여 호출하면 생성자 함수처럼 동작가능함.
때문에 일반적으로 생성자함수명은 첫 문자를 대문자로하여 혼동을 방지해야함.
// 생성자 함수
function Person(name) {
this.name = name;
}
var me = new Person('Lee');
console.log(me); // Person {name: "Lee"}
// new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다.
var you = Person('Kim');
console.log(you); // undefined
new연산자와 함께 함수를 호출하면 this바인딩이 메서드나 함수호출때와는 다르게 동작함.
new연산자와 함께 생성자 함수를 호출하면 아래와 같은 수순으로 동작함.
- 빈 객체 생성 및 this 바인딩.
생성자 함수를 호출하게 되면, 생성자 함수의 코드가 실행되기 전에 빈객체가 만들어진다.
이 비어있는 상태의 객체가 생성자 함수가 새로 생성한 객체이다. 이 후에 생성자 함수에서 사용되는 this는 이 비어있는 객체를 가리킨다. 그리고 생성된 빈 객체는 생성자 함수의 prototype 프로퍼티가 가르키는 객체를 자신의 프로토타입 객체로 설정함. - this를 통한 프로퍼티 생성.
생성된 빈 객체에 this를 사용하여 동적으로 프로퍼티나 메서드를 생성할 수 있다. this는 새로 생성된 객체를 가르키므로 this를 통해 생성된 프로퍼티나 메서드는 새로 생성된 객체에 추가됨. - 생성된 객체 반환.
반환문이 없는 경우, this에 바인딩 된 새로 생성한 객체가 반환됨. 명시적으로 this를 반환하여도 결과는 같음.
반환문이 this가 아닌 다른 객체를 명시적으로 반환하는 경우에는 this가 아닌 명시된 해당 객체가 반환됨.
이 때 this를 반환하지 않은 함수는 생성자 함수로서의 역할을 수행하지 못한다. 따라서 생성자 함수는 반환문을 명시적으로 사용하지 않는다.
객체 리터럴방식과 생성자 함수방식의 차이점은 프로토타입 객체에 있다.
// 객체 리터럴 방식
var foo = {
name: 'foo',
gender: 'male'
}
console.dir(foo);
// 생성자 함수 방식
function Person(name, gender) {
this.name = name;
this.gender = gender;
}
var me = new Person('Lee', 'male'); // dir은 객체의 속성을 계층구조로 출력할 때 쓴다.
console.dir(me);
var you = new Person('Kim', 'female');
console.dir(you);
- 객체 리터럴 방식의 경우 생성된 객체의 prototype 객체는 Object.prototype이다.
- 생성자 함수 방식의 경우 생성된 객체의 prototype 객체는 Person.prototype이다.
생성자 함수에 new 연산자를 붙이지 않고 호출할 경우
일반함수와 생성자함수간에 특별한 형식상 차이는 없으며 new연산자를 붙이면 일반함수도 생성자함수로 동작한다.
그런데 !
객체 생성을 목적으로 한 생성자함수를 new 연산자 없이 호출하거나 일반함수에 new를 붙여 호출하면 오류가 발생할 수 있다.
이는 일반함수와 생성자함수의 this바인딩 방식이 다르기 때문이다.
일반함수의 this는 전역변수인 window에 바인딩 되고, new연산자와 함께 생성자함수를 호출하면 this는 생성된 빈 객체에 바인딩 된다.
function Person(name) {
// new없이 호출하는 경우, 전역객체에 name 프로퍼티를 추가
this.name = name;
};
// 일반 함수로서 호출되었기 때문에 객체를 암묵적으로 생성하여 반환하지 않는다.
// 일반 함수의 this는 전역객체를 가리킨다.
var me = Person('Lee');
console.log(me); // undefined
console.log(window.name); // Lee
생성자 함수를 new없이 호출하면 함수Person 내부의 this는 전역객체를 가리키게 되고, name은 전역변수에 바인딩 된다. 또한 new와 함께 생성자 함수를 호출할 때 암묵적으로 반환하던 this도 반환하지 않으며 반환문이 없으므로 undefined를 반환한다.
일반함수와 생성자함수는 특별한 형식적 차이가 없기 때문에 일반적으로 생성자함수의 첫글자는 대문자로 표기하여 혼동을 방지하고자 한다. 그러나 이러한 노력에도 불구하고 실수가 발생할 수 있다.
이러한 위험성을 회피하기위해 사용되는 패턴(Object, Regex, Array)은 다음과 같다. 이 패턴은 다양한 라이브러리에서 광범위하게 사용된다.
참고로 대부분의 빌트인 생성자(Object, Regex, Array 등)는 new연산자와 함께 호출되었는지를 확인한 후 적절한 값을 반환한다.
// Scope-Safe Constructor Pattern
function A(arg) {
// 생성자 함수가 new 연산자와 함께 호출되면 함수의 선두에서 빈객체를 생성하고 this에 바인딩한다.
/*
this가 호출된 함수(arguments.callee, 본 예제의 경우 A)의 인스턴스가 아니면 new 연산자를 사용하지 않은 것이므로 이 경우 new와 함께 생성자 함수를 호출하여 인스턴스를 반환한다.
arguments.callee는 호출된 함수의 이름을 나타낸다. 이 예제의 경우 A로 표기하여도 문제없이 동작하지만 특정함수의 이름과 의존성을 없애기 위해서 arguments.callee를 사용하는 것이 좋다.
*/
if (!(this instanceof arguments.callee)) {
return new arguments.callee(arg);
}
// 프로퍼티 생성과 값의 할당
this.value = arg ? arg : 0;
}
var a = new A(100);
var b = A(10);
console.log(a.value);
console.log(b.value);
arguments.callee 를 이해하려면 또 한세월 할 것 같아서
오늘은 여기까지 하려고한다.
마지막요약!!!!!
new연산자와함께 생성자함수를 호출하는 경우 생성자함수 내부의 this는 생성자 함수를 호출할 때 생성된 인스턴스를 가리킨다. 따라서 위에 코드 중 A가 new연산자와 함게 생성자 함수로써 호출되면 A함수 내부의 this는 생성자 함수에 의해 생성된 인스턴스를 가리킨다.
다음 글은 apply/call/bind
'자바스크립트🔥' 카테고리의 다른 글
JS의 꽃 객체(1) (0) | 2021.07.22 |
---|---|
ES6 /인스턴스란? (0) | 2021.05.10 |
ES6 / 헷갈리는 그 이름 this 2편 / 메서드호출 (0) | 2021.05.10 |
ES6 / 헷갈리는 그 이름 this 1편 / 함수호출 (0) | 2021.05.09 |
ES6 / 화살표함수 (0) | 2021.05.09 |