객체 리터럴 VS 생성자 함수
우리가 지금까지 사용해왔던 객체 리터럴을 통해 객체를 생성해 직접 객체를 선언하는 방식과 생성자 함수를 이용해 객체를 생성하는데 어떤 차이가 있을까요? 결론부터 이야기하자면, 프로토타입 객체에 있어서 차이가 발생하게 됩니다.
Javascript에서의 객체 생성 기본 규칙은 "자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 [[Prototype]] 객체로 설정한다."입니다. 생성자 함수에서 강조했던 규칙이지만, 역시 객체 리터럴 방식의 객체 선언의 경우에도 적용되는 기본 규칙이죠. 여기서 생성자 함수의 경우는 알겠는데 객체 리터럴 방식일 경우에는 무엇이 생성자 함수가 될까요?
객체 리터럴 방식으로 객체를 생성할 때 이를 만들어내는 생성자 함수는 다름 아닌 Object(최상위 객체)가 됩니다. Object가 생성자 함수가 되기 때문에 객체 리터럴 방식으로 만들어진 객체는 Object.prototype을 자신의 [[Prototype]]으로 설정하게 됩니다. 생성자 함수를 통해 만들어진 객체는 역시 동일한 원리에 의해 생성자함수.prototype을 [[Prototype]]으로 설정하게 되는 것이죠.
아래에서, 간단하게 예제를 하나 살펴보도록 하겠습니다.
var dprVar = {
name: 'DPR Live',
age: 29,
gender: 'man'
};
// 생성자 함수
function Rapper(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
}
var jayparkVar = new Rapper("Jay Park", 30, 'man');
console.dir(dprVar);
console.dir(jayparkVar);
위의 경우 객체 dprVar는 객체 리터럴 방식으로, 객체 jayparkVar는 생성자 함수 Rapper( )를 통해 선언되었습니다. 각각의 객체는 동일하게 name / age / gender라는 프로퍼티를 갖고 있지만 객체 dprVar는 Obejct.prototype을, jayparkVar는 Rapper.prototype을 [[Prototype]] 객체로 설정하고 있는 차이점을 확인할 수 있습니다.
만일 new 연산자를 쓰지 않았다면?
여기서 만약에 위에서 선언해 사용한 Rapper( )라는 생성자 함수를 사용할 때, new 연산자를 사용하지 않는다면 어떤 현상이 벌어질까요? 한 번 살펴봅시다.
// 생성자 함수
function Rapper(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
}
var youngNrich = Rapper('superbee', 20, 'man');
console.log(youngNrich);
console.log(window.name);
console.log(window.age);
console.log(window.gender);
// 출력
// undefined
// superbee
// 20
// man
위 코드의 경우, Rapper( ) 사용 시 new 연산자를 쓰지 않았으므로 일반 함수의 실행 방식을 그대로 따르게 됩니다. 우선 해당 생성자 함수는 리턴 값이 명시되어있지 않기 때문에, undefined를 리턴하게 됩니다. 그래서 변수 youngNrich에는 undefined만 남게 됩니다.
그리고 해당 함수가 실행되었을 때, this는 우리가 앞선 아티클에서 살펴보았듯이 일반 함수이기 때문에 this에는 전역 객체(여기서는 window)에 바인딩되고 맙니다. 그래서 별개의 전역 변수 name / age / gender가 동적으로 생성되어버리는 결과를 낳게 되었습니다.
이와 같이 생성자 함수는 new 연산자를 사용하는 것을 전제로 하고 작성되는 코드로서 약속되어 있기 때문에, 생성자 함수 선언 시에는 첫 글자를 대문자로 선언하고 반드시 new 연산자를 사용해서 객체를 생성해야 합니다.
위와 같이 new를 실수로 붙이지 않아 생기는 실수를 방지하기 위해, 생성자 함수를 다음과 같이 선언하는 패턴도 존재합니다.
function A(arg){
if(!(this instanceof A))
return new A(arg);
this.value = arg ? arg : 0;
}
var a = A(10);
console.log(a.value);
// 출력
// 10
위의 패턴에서는 생성자 함수 A가 실행되는 상황에서, this가 생성자 함수의 인스턴스에 바인딩 되었는지 여부를 검사합니다. 즉 바인딩되어있는 상황이라면 new 연산자를 통해 실행된 상태이고, 아니라면 new 연산자 없이 실행된 상황이라고 보는 것이죠. 실수로 new를 빠뜨린 상황이라면, 강제로 new A(arg)를 리턴해 생성해줍니다. 그리고 this를 사용해 value를 동적으로 선언하게 됩니다.
아래에서 일부러 new를 쓰지않고 객체 a를 생성해 보았습니다. 그리고 a.value를 출력했는데 자연스럽게 10이 출력되었습니다.
참고로 this instanceof A 이 부분에서는 주로 A라는 함수명 대신 this instanceof arguments.callee를 사용해 함수명과 관계없이 인스턴스 여부를 검사할 수 있습니다.
'Programming > Javascript' 카테고리의 다른 글
9. Javascript에서의 함수 리턴 (0) | 2022.10.17 |
---|---|
8. Javascript 함수 호출 (5) - call, apply 메서드와 this 바인딩 (0) | 2022.10.15 |
8. Javascript 함수 호출 (4) - 생성자 함수 호출 시 this 바인딩 1 (0) | 2022.10.12 |
8. Javascript 함수 호출 (3) - 함수를 호출할 때 this 바인딩 (0) | 2022.10.07 |
8. Javascript 함수 호출 (2) - 객체 메서드 호출 시 this 바인딩 (2) | 2022.10.04 |