JS (17) - 생성자 함수에 의한 객체 생성

최조니·2022년 7월 6일
0

JavaScript

목록 보기
14/36

17.1 Object 생성자 함수

  • new 연산자와 함께 Object 생성자 함수를 호출하면 빈 객체를 생성하여 반환

  • 빈 객체를 생성한 이후 프로퍼티 또는 메서드를 추가하여 객체 완성

// 빈 객체 생성
const person = new Object();

// 프로퍼티 추가
person.name = 'Lee';
person.sayHello = function() {
  console.log('Hi! My name is ' + this.name);
};

console.log(person);	// {name: "Lee", sayHello: f}
person.sayHello();		// Hi! My name is Lee

생성자 함수란❓

new 연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수
생성자 함수에 의해 생성된 객체를 인스턴스라고 함

  • Object 생성자 함수 외에도
    String, Number, Boolean, Function, Array, Date, RegExp, promise 등의 빌트인 생성자 함수 제공
// String 생성자 함수에 의한 String 객체 생성
const strObj = new String('Lee');
console.log(typeof strObj);		// object
console.log(strObj);			// String {"Lee"}

// Number 생성자 함수에 의한 Number 객체 생성
const numObj = new Number(123);
console.log(typeof numObj);		// object
console.log(numObj);			// Number {123}

// Boolean 생성자 함수에 의한 Boolean 객체 생성
const boolObj = new Boolean(true);
console.log(typeof boolObj);	// object
console.log(boolObj);			// Boolean {true}

// Function 생성자 함수에 의한 Function 객체(함수) 생성
const func = new Function('x', 'return x * x');
console.log(typeof func);		// function
console.dir(func);				// f anonymous(x)

// Array 생성자 함수에 의한 Array 객체(배열) 생성
const arr = new Array(1, 2, 3);
console.log(typeof arr);		// object
console.log(arr);				// [1, 2, 3]

// RegExp 생성자 함수에 의한 RegExp(정규 표현식) 객체 생성
const regExp = new RegExp(/ab+c/i);
console.log(typeof regExp);		// object
console.log(regExp);			// /ab+c/i

// Date 생성자 함수에 의한 Date 객체 생성
const date = new Date();
console.log(typeof date);		// object
console.log(date);				// Thu Jul 07 2022 15:13:49 GMT+0900 (한국 표준시)
사실 객체 리터럴을 사용하는 것이 더 간편함❗️

17.2 생성자 함수

1) 객체 리터럴에 의한 객체 생성 방식의 문제점

  • 객체 리터럴에 의한 객체 생성 방식은 직관적이고 간편하지만, 단 하나의 객체만 생성

  • 동일한 프로퍼티를 갖는 객체를 여러 개 생성해야 하는 경우, 매번 같은 프로퍼티를 기술해야 함 (비효율적)

    • 객체마다 프로퍼티 값이 다를 수 있지만, 메서드는 내용이 동일한 경우가 일반적
const circle1 = {
  radius: 5,
  getDiameter() {
    return 2 * this.radius;
  }
};
console.log(circle1.getDiameter());		// 10

const circle2 = {
  radius: 10,
  getDiameter() {
    return 2 * this.radius;
  }
};
console.log(circle2.getDiameter());		// 20

2) 생성자 함수에 의한 객체 생성 방식의 장점

  • 생성자 함수를 사용하여 프로퍼티 구조가 동일한 객체 여러 개를 간편하게 생성할 수 있음

  • 일반함수와 동일한 방법으로 생성자 함수를 정의하고, new 연산자와 함께 호출하면 해당 함수는 생성자 함수로 동작

// 생성자 함수
function Circle(radius) {
  this.radius = raduis;
  this.getDiameter = function() {
    return 2 * this.radius;
  };
}

const circle1 = new Circle(5);
const circle2 = new Circle(10);

console.log(circle1.getDiameter());		// 10
console.log(circle2.getDiameter());		// 20
  • 만일 new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수가 아닌 일반 함수로 동작
const circle3 = Circle(15);

console.log(circle3);	// undefined, 반환문이 없으므로 암묵적으로 undefined 반환
console.log(radius);	// 15, 일반 함수로서 호출된 Circle 내의 this는 전역 객체를 가리킴

this : 객체 자신의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수

this가 가리키는 값은 함수 호출 방식에 따라 동적으로 결정됨

함수 호출 방식 this가 가리키는 값
일반 함수로서 호출 전역 객체
메서드로서 호출 메서드를 호출한 객체
생성자 함수로서 호출 생성자 함수가 생성할 인스턴스

3) 생성자 함수의 인스턴스 생성 과정

  • 생성자 함수는 인스턴스를 생성하고, 생성된 인스턴스를 초기화(프로퍼티 추가 및 초기값 할당) 해야 함
// 생성자 함수
function Circle(radius) {
  // 인스턴스 초기화
  this.radius = radius;
  this.getDiameter = function() {
    return 2 * this.radius;
  };
}

// 인스턴스 생성
const circle1 = new Circle(5);	
  • new 연산자와 함께 생성자 함수를 호출하면 자바스크립트 엔진은 다음 과정을 거쳐 암묵적으로 인스턴스를 생성하고 초기화한 후 암묵적으로 인스턴스를 반환함
  1. 인스턴스 생성과 this 바인딩
  • 암묵적으로 빈 객체 생성 (생성자 함수가 생성한 인스턴스)

  • 암묵적으로 생성된 빈 객체(인스턴스)는 this에 바인딩
    : 생성자 함수 내부의 this가 생성할 인스턴스를 가르키는 이유❗️

  • 이 처리는 런타임 이전에 실행


  1. 인스턴스 초기화
  • 생성자 함수에 기술되어 있는 코드가 한 줄씩 실행되어 this에 바인딩되어 있는 인스턴스 초기화

  • this에 바인딩되어 있는 인스턴스에 프로퍼티나 메서드를 추가하고 생성자 함수가 인수로 전달받은 초기값을 인스턴스 프로퍼티에 할당하여 초기화하거나 고정 값 할당


  1. 인스턴스 반환
  • 생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this를 암묵적으로 반환
function Circle(radius) {
  // 1. 암묵적으로 빈 객체(인스턴스) 생성, this에 바인딩
  
  // 2. this에 바인딩되어 있는 인스턴스 초기화
  this.radius = radius;
  this.getDiameter = function() {
    return 2 * radius;
  };
  
  // 3. 완성된 인스턴스가 바인딩된 this를 암묵적으로 반환
}

// 인스턴스 생성, Circle 생성자 함수는 암묵적으로 this를 반환
const circle = new Circle(1);
console.log(circle);	// Circle {radius: 1, getDiameter: f}
  • 만약 this가 아닌 다른 객체를 명시적으로 반환한다면 ❓
    this가 반환되지 못하고 return문에 명시한 객체가 반환됨
function Circle(radius) {
  this.radius = radius;
  this.getDiameter = function() {
    return 2 * radius;
  };
  
  return {};
}

const circle = new Circle(1);
console.log(circle);	// {}
  • 명시적으로 원시값을 반환하면 ❓
    원시 값은 무시되고 암묵적으로 this가 반환
function Circle(radius) {
  this.radius = radius;
  this.getDiameter = function() {
    return 2 * radius;
  };
  
  return 100;
}

const circle = new Circle(1);
console.log(circle);	// Circle {radius: 1, getDiameter: f}

4) 내부 메서드 [[Call]]과 [[Construct]]

  • 함수는 객체이므로 일반 객체와 동일하게 동작하지만, 일반 객체와 달리 함수는 호출할 수 있음
    [[Environment]], [[FormalParameters]] 등의 내부 슬롯과 [[Call]], [[Construct]] 같은 내부 메서드를 추가적으로 가짐

  • 함수가 일반 함수로서 호출되면, 함수 객체의 내부 메서드 [[Call]]이 호출

    • 내부 매서드 [[Call]]을 갖는 함수 객체를 callable
  • new 연산자와 함께 생성자 함수로서 호출되면, 내부 메서드 [[Construct]] 호출

    • 내부 매서드 [[Construct]]을 갖는 함수 객체를 constructor, 그렇지 않은 객체를 non-constructor
function foo() {}

// 일반적인 함수로서 호출 : [[Call]] 호출
foo();

// 생성자 함수로서 호출 : [[Construct]] 호출
new foo();

callable : 호출할 수 있는 객체 (함수)
constructor : 생성자 함수로 호출할 수 있는 함수
non-constructor : 객체를 생성자 함수로서 호출할 수 없는 함수

함수 객체는 callable이면서 constructor이거나 non-constructor


5) constructor와 non-constructor의 구분

함수 정의를 평가하여 함수 객체를 생성할 때 함수 정의 방식에 따라 constructornon-constructor로 구분

  • constructor
    함수 선언문, 함수 표현식, 클래스

  • non-constructor
    메서드(ES6 메서드 축약 표현), 화살표 함수

// 일반 함수 정의 : 함수 선언문, 함수 표현식
function foo() {};
const bar = function () {};
const baz = {
  x: function () {}
};

// 일반함수로 정의된 함수만 constructor
new foo();
new bar();
new baz.x();

// 화살표 함수 정의
const arrow = () => {};
new arrow();	// TypeError: arrow is not a constructor

// 메서드(ES6 메서드 축약 표현)
const obj = {
  x() {}
};

new obj.x();	// TypeError: obj.x is not a constructor

6) new 연산자

  • 일반 함수와 생성자 함수에 특별한 형식적 차이 X

  • new 연산자와 함께 함수를 호출하면 해당 함수는 생성자 함수로 동작
    [[Construct]] 호출
    → 즉, new 연산자와 함께 호출하는 함수는 constructor여야 함

// 생성자 함수로서 정의하지 않은 일반 함수
function add(x, y) {
  return x + y;
}

// 생성자 함수로서 정의하지 않은 일반 함수를 new 연산자와 함께 호출
let inst = new add();
console.log(inst);	// {} : 함수가 객체를 반환하지 않았으므로 반환문이 무시

// 객체를 반환하는 일반 함수
function createUser(name, role) {
  return { name, role };
}

// 일반함수를 new연산자와 함께 호출
inst = new createUser('Lee', 'admin');
console.log(inst);	// {name: "Lee", role: "admin"}	: 함수가 생성한 객체를 반환
  • new 연산자 없이 생성자 함수를 호출하면 일반 함수가 호출됨
    [[Call]]메서드 호출됨
function Circle(radius) {
  this.radius = radius;
  this.getDiameter = function() {
    return 2 * this.radius;
  };
}

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

7) new.target

  • 생성자 함수가 new 연산자 없이 호출되는 것을 방지하기 위해 ES6에서는 new.target 지원

  • 함수 내부에서 new.target을 사용하면 new 연산자와 함께 생성자 함수로서 호출되었는지 확인할 수 있음

    • 생성자 함수로서 호출되면 함수 내부의 new.target함수 자신을 가리킴
    • 일반 함수로서 호출되면 함수 내부의 new.targetundefined
function Circle(radius) {
  if (!new.target){
    // new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스 반환
    return new Circle(radius);
  }
  
  this.radius = radius;
  this.getDiameter = function() {
    return 2 * radius;
  };
}

// new 연산자 없이 생성자 함수를 호출하여도 new.target을 통해 생성자 함수로 호출
const circle = Circle(5);
console.log(circle.getDiameter());	// 10

new.target은 IE에서 지원하지 않음 ❗️
    → 스코프 세이프 생성자 패턴 사용

function Circle(radius) {
  if (!(this instanceof Circle)){
    return new Circle(radius);
  }
  
  this.radius = radius;
  this.getDiameter = function() {
    return 2 * radius;
  };
}

const circle = Circle(5);
  • 대부분의 빌트인 생성자 함수 (Object, String, Number, Boolean, Function, Array, Date, RegExp, Promise 등)는 new 연산자와 함께 호출되었는지 확인 후 적절한 값 반환
    • Object와 Function 생성자 함수는 new 연산자 없이 호출해도 new 연산자와 함께 호출했을 때와 동일하게 동작
    • String, Number, Boolean 생성자 함수는 new 연산자와 함께 호출했을 때 String, Number, Boolean 객체를 생성하여 반환하지만 new 연산자 없이 호출하면 문자열, 숫자, 불리언 값을 반환
let obj = new Object();
console.log(obj);

obj = Object();
console.log(obj);

let f = new Function('x', 'return x * x');
console.log(f);

f = Function('x', 'return x * x');
console.log(f);
const str = String(123);
console.log(str, typeof str);	// 123 string

const num = Number('123');
console.log(num, typeof num);	// 123 Number

const bool = Boolean('true');
console.log(bool, typeof bool);	// true Boolean
profile
Hello zoni-World ! (◍ᐡ₃ᐡ◍)

0개의 댓글