[JavaScript] 생성자 함수와 new

Letmegooutside·2022년 1월 20일
0

JavaScript

목록 보기
11/25
post-thumbnail

객체를 생성하는 방법

객체 리터럴 사용

중괄호({})를 사용하여 객체를 생성한다.

// 프로퍼티가 추가된 객체 생성
var obj = {
    name: "kim",
    getName: function () {
        console.log(this.name);
    }
};
// 빈 객체 생성
var obj = {};

생성자 함수 사용

  • 관례상 함수 이름의 첫글자는 대문자로 시작한다.
  • 호출 시 new 연산자를 붙여서 실행한다.
function Person(name) {
    this.name = name;
    this.showName = function () {
        console.log(`My name is ${this.name}`);
    };
}

var person = new Person("Son");
person.showName();

객체 리터럴을 사용하는 방식도 내부적으로는 생성자 함수와 new 연산자를 사용하는 방식으로 실행된다.
(빌트인(Built-in) 함수인 Object 생성자 함수를 사용한다.)

var obj = {};
// 위의 코드는 내부적으로 아래의 방식으로 실행된다.
var obj = new Object();

생성자 함수

객체를 생성하는데 사용하는 함수

생성자 함수는 객체를 생성하는데 사용하는 함수로 new 연산자와 호출할 경우 객체를 생성할 수 있다.

new 연산자 : 사용자 정의 객체 타입 또는 내장 객체 타입의 인스턴스를 생성하는 연산자

아래와 같이 사용할 수 있다.

function Cons(name, age) {
    this.name = name;
    this.age  = age;
}

var obj = new Cons("Lee", "21");

/*
obj = {
    name: "Lee",
    age: 21
}
이런 형태의 객체가 생성된다.
*/

생성자 함수는 함수 정의차원이 아니라 그냥 용도에 따라 구분한 개념이므로 일반 함수처럼 호출해도 에러는 발생하지 않는다.

var obj = Cons("Lee", "21"); // undefined

다만 이 경우 Cons()함수 내부의 this는 전역객체를 가리키고 있으므로, 전역객체에 nameage 프로퍼티가 추가된다.

new 연산자 강제하기

new.target 속성(property)을 통해 함수 또는 생성자가 new 연산자를 사용하여 호출됐는지를 감지할 수 있다.
new 연산자로 인스턴스화된 생성자 및 함수에서, new.target은 생성자 또는 함수 참조를 반환하고, 일반 함수 호출에서는 undefined를 반환한다.

function Foo() {
  if (!new.target) { throw 'Foo() must be called with new'; }
}

try {
  Foo();
} catch (e) {
  console.log(e);
  // expected output: "Foo() must be called with new"
}

new 연산자의 동작

new 연산자는 빈 객체 생성과 함수 호출, 객체 반환을 일으킨다.

new Cons("Lee", 21)을 사용해서 함수를 실행하면 아래와 같은 알고리즘이 동작한다.
1. 빈 객체를 생성하고 this가 빈 객체를 가리키게 한다.
2. 함수를 실행한다.
3. this를 반환한다.

function Cons(name) {
    // 빈 객체가 생성되고 this에 저장
    this = {};

    // 함수 실행 
    // this는 객체({})기 때문에 this.name = name 구문은 객체에 name 값을 넣는 것과 같다.
    this.name = name;
    this.age  = age;
    
    return this;
}

이 설명 보다는 applycall함수를 이용해 설명하는게 좀 더 직관적이고 실제 작동원리에도 더 가까울 것이다.

var temp = {};
// apply를 통해 Cons의 this값을 temp로 변경하여 함수를 실행한다.
var obj = Cons.apply(temp);

생성자 함수에는 기본적으로 return문이 존재하지 않지만 위에서 말한대로 new연산자가 함수반환을 일으키기때문에 obj라는 변수에 생성된 객체가 들어가게된다.

생성자 함수에서의 return문

function plus(num1, num2){
    var num = num1 + num2;
    return num;
}

var variable = new plus(1, 2);
console.log(variable); // {}

이 코드의 동작을 살펴보자
먼저 빈 객체를 생성하고, 함수를 호출해서 함수 내부 this에 방금 생성한 빈 객체를 대입할 것이다.
그런데 plus 함수에는 this가 없다. 그래서 빈 객체에는 아무런 속성이 추가되지않고 빈 객체가 반환되게 될 것이다.

빈 객체가 반환된다는 것은 return num; 이 부분을 무시하고 빈 객체를 반환하는 return문이 실행됐다는 것이다.
이처럼 기본타입을 반환하는 함수는 new연산자를 사용했을 때 new 연산자로 생성된 객체를 반환하도록 하는 반면, 객체를 반환하는 함수는 return문을 무시하지 못한다.

예를 들어 아래 코드에서는 생성자 함수는 return문에 명시된 객체를 반환할 것이다.

function plus(num1, num2) {
    var num = num1 + num2;
    this.result = num;
    plusObj = this;

    return {
        name: "Lee",
        age: 21
    }
}
var obj = new plus(1, 2); // 생성자 함수의  return문에 명시된 객체 반환

console.log(plusObj.result); // 3

이 때 plubObj는 함수의 런타임에 전역변수로 선언되며 new 연산자로 생성한 객체를 저장하게된다.
return은 객체 리터럴을 반환하고 있기 때문에 objnew 연산자가 생성한 객체를 저장하지않는다.
하지만 전역으로 정의된 plusObjnew 연산자가 실행되면서 생성된 객체를 담고 있기 때문에 다른 객체를 리턴하더라도 new로 인해 생성된 객체는 분명히 존재한다는것을 확인할 수 있다.

요약

  • return문이 없을 경우 : this를 반환한다.
  • return 값이 객체일 경우 : 해당 객체를 반환한다.
  • return 값이 기본 변수일 경우 : 기본 변수 반환을 무시하고 this를 반환한다.

생성자 함수를 사용하는 이유

객체를 재사용하기 위함으로, 리터럴 객체를 만드는 방법보다 수고를 덜 수 있다.

var person1 = new Cons("Lee",21);
var person2 = new Cons("Park",2);
var person3 = new Cons("Kim",29);



Reference
https://bamdule.tistory.com/196
https://multifrontgarden.tistory.com/69

0개의 댓글