[JavaScript]_Contructor

hanseungjune·2023년 7월 6일
0

JavaScript

목록 보기
85/87
post-thumbnail

📃 관련 링크

🖨️ 번역본

constructor

생성자(Constructor) 메서드는 클래스의 특별한 메서드로서 해당 클래스의 객체 인스턴스를 생성하고 초기화하는 역할을 합니다.

class Polygon {
  constructor() {
    this.name = 'Polygon';
  }
}

const poly1 = new Polygon();

console.log(poly1.name);
// 예상 출력: "Polygon"

생성자 메서드의 문법은 다음과 같습니다.

constructor() { /* ... */ }
constructor(argument0) { /* ... */ }
constructor(argument0, argument1) { /* ... */ }
constructor(argument0, argument1, /* ..., */ argumentN) { /* ... */ }

일부 추가적인 문법 제약사항이 있습니다.

  • constructor라는 이름의 클래스 메서드는 게터(getter), 세터(setter), async, generator로 선언될 수 없습니다.
  • 클래스는 두 개 이상의 constructor 메서드를 가질 수 없습니다.

생성자는 인스턴스화된 객체에 대해 다른 메서드를 호출하기 전에 수행해야 할 사용자 정의 초기화 작업을 제공합니다.

class Person {
  constructor(name) {
    this.name = name;
  }

  introduce() {
    console.log(`안녕하세요, 제 이름은 ${this.name}입니다.`);
  }
}

const otto = new Person("Otto");

otto.introduce(); // 안녕하세요, 제 이름은 Otto입니다.

사용자 정의 생성자를 제공하지 않는 경우, 자동으로 기본 생성자가 제공됩니다. 기본 생성자는 기본 클래스인 경우 비어있습니다.

constructor() {}
constructor(...args) {
  super(...args);
}

참고: 위의 예시와 같은 명시적인 생성자와 기본 생성자의 차이점은 후자가 실제로 배열 반복자를 argument spreading을 통해 호출하지 않는다는 것입니다.

이로 인해 다음과 같은 코드가 작동합니다.

class ValidationError extends Error {
  printCustomerMessage() {
    return `Validation failed :-( (details: ${this.message})`;
  }
}

try {
  throw new ValidationError("유효하지 않은 전화번호입니다");
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(error.name); // ValidationError 대신 Error가 출력됩니다!
    console.log(error.printCustomerMessage());
  } else {
    console.log("알 수 없는 오류", error);
    throw error;
  }
}

ValidationError 클래스는 명시적인 생성자가 필요하지 않으므로 사용자 정의 생성자를 제공하지 않습니다. 기본 생성자가 부모 Error를 인수로 초기화하는 초기화 작업을 처리합니다.

그러나 사용자 정의 생성자를 제공하고 해당 클래스가 어떤 부모 클래스를 상속받는 경우, super()를 사용하여 명시적으로 부모 클래스 생성자를 호출해야 합니다. 예를 들면 다음과 같습니다.

class ValidationError extends Error {
  constructor(message) {
    super(message); // 부모 클래스 생성자 호출
    this.name = "ValidationError";
    this.code = "42";
  }

  printCustomerMessage() {
    return `Validation failed :-( (details: ${this.message}, code: ${this.code})`;
  }
}

try {
  throw new ValidationError("유효하지 않은 전화번호입니다");
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(error.name); // 이제 ValidationError이 출력됩니다!
    console.log(error.printCustomerMessage());
  } else {
    console.log("알 수 없는 오류", error);
    throw error;
  }
}

클래스의 생성자에는 반환 값이 있을 수 있습니다. 기본 클래스는 생성자에서 아무것도 반환할 수 있지만, 파생 클래스는 객체 또는 undefined를 반환해야 하며 그렇지 않으면 TypeError가 발생합니다.

class ParentClass {
  constructor() {
    return 1;
  }
}

console.log(new ParentClass()); // ParentClass {}
// 반환 값은 객체가 아니므로 무시됩니다
// 이는 함수 생성자와 일관성이 있습니다

class ChildClass extends ParentClass {
  constructor() {
    return 1;
  }
}

console.log(new ChildClass()); // TypeError: 파생 클래스의 생성자는 객체 또는 undefined만 반환할 수 있습니다

부모 클래스의 생성자가 객체를 반환하는 경우, 해당 객체는 파생 클래스의 class 필드에 정의될 this 값으로 사용됩니다. 이 기법은 "return overriding"이라고 불리며 파생 클래스의 필드(비공개 필드 포함)가 관련 없는 객체에 정의될 수 있게 합니다.

생성자는 일반적인 메서드 구문을 따르므로 매개변수 기본값, 나머지 매개변수 등을 모두 사용할 수 있습니다.

class Person {
  constructor(name = "익명") {
    this.name = name;
  }
  introduce() {
    console.log(`안녕하세요, 제 이름은 ${this.name}입니다.`);
  }
}

const person = new Person();
person.introduce(); // 안녕하세요, 제 이름은 익명입니다.

생성자는 반드시 리터럴 이름이어야 합니다. 계산된 속성은 생성자로 사용할 수 없습니다.

class Foo {
  // 이것은 계산된 속성입니다. 생성자로 인식되지 않습니다.
  ["constructor"]() {
    console.log("호출됨");
    this.a = 1;
  }
}

const foo = new Foo(); // 로그 없음
console.log(foo); // Foo {}
foo.constructor(); // "호출됨" 출력
console.log(foo); // Foo { a: 1 }

async 메서드, generator 메서드, 접근자(accessor), 클래스 필드는 생성자로 호출될 수 없습니다. 또한 private 이름은 #constructor로 호출될 수 없습니다. constructor라는 이름의 멤버는 일반 메서드이어야 합니다.

예제

생성자 사용하기

이 코드 스니펫은 클래스 예제에서 가져온 것입니다 (실제 데모).

class Square extends Polygon {
  constructor(length) {
    // 여기서 Polygon의 너비와 높이에 제공된 길이로
    // 부모 클래스의 생성자를 호출합니다.
    super(length, length);
    // 참고: 파생 클래스에서 `super()`를 사용하기 전에
    // `this`를 사용할 수 있도록 해야 합니다.
    // 이를 생략하면 ReferenceError가 발생합니다.
    this.name = "Square";
  }

  get area() {
    return this.height * this.width;
  }

  set area(value) {
    this.height = value ** 0.5;
    this.width = value ** 0.5;
  }
}

다른 프로토타입에 바인딩된 생성자에서 super 호출하기

super()는 현재 클래스의 프로토타입의 생성자를 호출합니다. 현재 클래스 자체의 프로토타입을 변경하면 super()는 새로운 프로토타입의 생성자를 호출합니다. 현재 클래스의 prototype 속성의 프로토타입을 변경해도 super()가 호출하는 생성자는 영향을 받지 않습니다.

class Polygon {
  constructor() {
    this.name = "Polygon";
  }
}

class Rectangle {
  constructor() {
    this.name = "Rectangle";
  }
}

class Square extends Polygon {
  constructor() {
    super();
  }
}

// Square을 Polygon이 아닌 Rectangle (기본 클래스)을 상속하도록 변경
Object.setPrototypeOf(Square, Rectangle);

const newInstance = new Square();

// newInstance는 여전히 Polygon의 인스턴스입니다. Square.prototype을
// 변경하지 않았으므로 newInstance의 프로토타입 체인은 여전히 다음과 같습니다.
//   newInstance --> Square.prototype --> Polygon.prototype
console.log(newInstance instanceof Polygon); // true
console.log(newInstance instanceof Rectangle); // false

// 그러나 super()가 Rectangle로 호출되므로 newInstance의 name 속성은
// Rectangle의 로직으로 초기화됩니다.
console.log(newInstance.name); // Rectangle

후기

사실 지금 이렇게 번역했지만... 내용을 전부 이해하지는 못했다. 그래도 나중에 다시 번역해서 이해하기 보다는 미리 번역해두면 더 편할 것 같아서 미리 번역해두는 거다. 일단 간단하게 예습 정도 해야할 일이 있어서 이렇게 글을 작성했고 다음에 더 깊게 공부하게 된다면, 해당 자료를 꼭 참고해야겠다.

profile
필요하다면 공부하는 개발자, 한승준

0개의 댓글