Skip to content

Latest commit

 

History

History
306 lines (235 loc) · 10.4 KB

File metadata and controls

306 lines (235 loc) · 10.4 KB

17.1 Object 생성자 함수

생성자 함수 == new 연산자와 함께 호출하여 객체를 생성하는 함수 (String, Number, Boolean, Function, Array, Date, RegExp, Promise등) 인스턴스 == 생성자 함숭 의해 생성된 객체

  • new 연산자와 함께 Object 생성자 함수를 호출하면 빈 객체를 생성하여 반환함.
  • 빈 객체를 생성한 이후 프로퍼티, 메서드를 추가하여 객체를 완성함.

17.2 생성자 함수

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

  • 객체 리터럴에 의한 객체 생성 방식은 단 하나의 객체만 생성 가능
  • "객체"는 "프로퍼티"를 통해 객체의 고유 상태를 표현함 "메소드"는 상태 데이터인 "프로퍼티"를 참조하고 조작하는 동작을 표현함
  • 동일한 프로퍼티를 갖는 객체를 여러 개 생성할 경우 매번 같은 프로퍼티를 기술해야하므로 비효율적이다. => 즉, circle1, circle2getDiameter처럼 동일한 프로퍼티 구조임에도 따로 프로퍼티와 메서드를 기술해야함.
const circle1 = {
  radius: 5,
  getDiameter() {
    return 2 * this.radius;
  },
};

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

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

  • 객체(인스턴스)를 생성하기 위한 클래스처럼 프로퍼티 구조가 동일한 객체 여러 개를 생성할 수 있다.
  • 생성자 함수를 new 연산자와 함께 호출 시, 해당 함수는 생정자 함수로 동작한다. new 연산자를 호출하지 않으면, 일반 함수로 동작한다.
// 생성자 함수
function Circle(radius) {
  // 생성자 함수 내부의 this는 인스턴스를 가리킴
  this.radius = radius;
  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

this 객체 자신의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수로 this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다.

17.2.3 생성자 함수의 인스턴스 생성 과정

  • 생성자 함수의 역할 == 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 클래스로 동작하여, 1) 인스턴스를 생성하기 2) 생성된 인스턴스를 초기화하기
function Circle(radius) {
  // 인스턴스 초기화
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };
}

// 인스턴스 생성
const circle1 = new Circle(5);

=> this에 프로퍼티 추가 (확인) 전달된 인수를 프로퍼티 초기값으로서 할당하여 인스턴스 초기화(확인) 인스턴스를 생성하고 반환하는 건 자바스크립트 엔진이 암묵적인 처리를 통해 이뤄짐

1. 인스턴스 생성과 this 바인딩

암묵적으로 빈 인스턴스 객체가 생성됨 인스턴스는 this에 바인딩되어 생성자 함수 내부의 this가 생성자 함수가 생성할 인스턴스에 가리킴

바인딩 식별자와 값을 연결하는 과정 변수 선언은 변수 이름(식별자)과 확보된 메모리 공간의 주소를 바인딩하는 것

function Circle(radius) {
  // 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
  console.log(this); // Circle {}
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };
}

2. 인스턴스 초기화

생성자 함수에 기술되어있는 코드가 한 줄씩 실행되어 this에 바인딩 되어있는 인스턴스를 초기화함. 즉, this에 바인딩되어 있는 인스턴스에 프로퍼티나 메서드를 추가하고 생성자 함수가 인수로 전달받은 초기값을 인스턴스 프로퍼티에 할당하여 초기화하거나 고정값을 할당한다.

function Circle(radius) {
  // 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
  // 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };
}

3. 인스턴스 반환

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

// 인스턴스 생성. Circle 생성자 함수는 암묵적으로 this를 반환한다.
const circle = new Circle(1);
console.log(circle); // Circle {radius: 1, getDiameter: f}
  1. 만약에 this가 아닌 다른 객체를 명시적으로 반환 시 return문에 명시한 객체가 반환된다.
function Circle(radius) {
  // 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
  // 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };
  // 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
  // 명시적으로 객체를 반환하면 암묵적인 this 반환이 무시된다.
  return {};
}

// 인스턴스 생성. Circle 생성자 함수는 명시적으로 반환된 객체를 반환한다.
const circle = new Circle(1);
console.log(circle); // {}
  1. 명시적으로 원시 값을 반환하면 원시 값 반환은 무시되고 암묵적으로 this가 반환된다.
function Circle(radius) {
  // 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
  // 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };
  // 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
  // 명시적으로 원시 값을 반환하면 원시 값 반홚은 무시되고 암묵적으로 this가 반환된다.
  return 100;
}

// 인스턴스 생성. Circle 생성자 함수는 명시적으로 반환된 객체를 반환한다.
const circle = new Circle(1);
console.log(circle); // Circle {radius: 1, getDiameter: f}

17.2.4 내부 메서드[[Call]]과 [[Construct]]

  • 내부 메서드로 함수 호출과 객체 생성과 관련됨.
  • 함수가 일반 함수로서 호출되면 함수 객체의 내부 메서드 **[[Call]]**이 호출됨. new 연산자와 함께 생성자 함수로서 호출되면 내부 메서드 **[[Construct]]**가 호출됨.
function foo() {}

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

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

=> 모든 함수 객체는 호출할 수 있지만, 모든 함수 객체를 생성자 함수로서 호출할 수 있는 것은 아니다.

17.2.5 constructor와 non-constructor 구분

  • constructor: 함수 선언문, 함수 표현식, 클래스
  • non-constructor: 메서드(ES6), 화살표 함수

[[Call]]과 [[Construct]]는 ES6 이후에도 계속 사용됩니다. 다만, ES6 이후의 문법에서는 class와 arrow function이 도입되면서 동작 방식이 조금 달라졌습니다. 1. 클래스(Class)와 [[Construct]]

  • class 키워드로 정의된 클래스는 반드시 new와 함께 호출해야 합니다.
  • 일반 함수처럼 호출하면 TypeError가 발생합니다.
class Car {
  constructor(model) {
    this.model = model;
  }
}

// new 없이 호출하면 에러 발생
// Car("Tesla"); // TypeError: Class constructor Car cannot be invoked without 'new'

const myCar = new Car("Tesla"); // [[Construct]]가 실행됨
console.log(myCar.model); // "Tesla"

2. 화살표 함수(Arrow Function)와 [[Call]]

  • 화살표 함수는 [[Call]]은 있지만 [[Construct]]가 없습니다. 즉, new 키워드로 호출할 수 없습니다.
const ArrowFunc = () => {};
ArrowFunc(); // 정상 실행 ([[Call]]이 실행됨)

// new ArrowFunc(); // TypeError: ArrowFunc is not a constructor

17.2.6 new 연산자

  • 일반 함수와 생성자 함수에 형식적인 차이가 없어, new 연산자와 함께 함수를 호출하면 해당 함수는 생성자 함수로 동작한다.
  • 즉, 함수 객체 내부 메서드 [[Constructor]]가 호출된다.
  • new 연산자와 함께 호출하는 함수는 constructor이어야 함.(new 연산자는 생성자 함수를 호출할 때 사용됨.)

1. new 연산자 없이 함수 호출 [[Call]] 실행됨

function showThis() {
    console.log(this);
}

showThis(); // 일반 호출 (브라우저: window, Node.js: global)

==============================

"use strict";

function showThis() {
    console.log(this);
}

showThis(); // undefined
  1. 엄격 모드('use strict')가 아닐 때 → this는 window(브라우저) 또는 global(Node.js)
  2. 엄격 모드('use strict')일 때 → this는 undefined

2. new 연산자로 함수 호출 [[Construct]] 실행됨

function Person(name) {
  this.name = name; // 새로운 객체의 프로퍼티가 됨
  console.log(this); // 새로운 객체 출력
}

const p1 = new Person("Alice"); // new를 사용했으므로 새로운 객체 생성
console.log(p1.name); // "Alice"

17.2.7 new.target

ES6 때 도입된 언어

  • new 연산자와 함께 생성자 함수로서 호출되면, 함수 내부의 new.target은 함수 자신을 가리킴.
  • new 연산자 없이 일반 함수로서 호출된 함수 내부의 new.target은 undefined이다.
function Circle(radius) {
  // new 연산자와 함께 호출되지 않으면, new.target == undefined
  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());

=> 대신 !(this instance of Circle)을 사용해도 됨