ARTICLE | [JS] 클래스

noopy·2021년 11월 16일
1

✏️ STUDY

목록 보기
7/10

팀원들과 함께하는 모던 JS 딥다이브 스터디 9차 💕

클래스 필드와 constructor 중복 시

클래스 필드의 이름과 constructor 내부에서 this에 할당한 프로퍼티 이름이 같을 때 어느 것이 우선시될까?

  • 예상: 클래스 필드를 constructor 아래에 선언했다면 클래스 필드가 우선시 될 것


클래스 필드가 선언된 위치에 상관없이 이름이 중복될 경우 constructor가 항상 우선시되는 걸 확인할 수 있었다.
클래스 필드 또한 인스턴스의 프로퍼티로 할당이 될 텐데, 클래스 필드를 아래에 선언했음에도 불구하고 constructor가 왜 우선시될까?

인스턴스 자신의 메서드를 갖고 싶을 때

생성자는 static 메서드로 자신의 메서드를 갖지만 인스턴스는 어떻게 자신의 메서드를 가질까?
클래스 필드를 통해 인스턴스 메서드를 정의할 수 있다.
모든 클래스 필드는 인스턴스의 프로퍼티가 되기 때문이다.

class Person {
 
  name = 'Jeongs'
  getName = function () {
    return this.name
  }
}
const me = new Person()
me.getName() // 'Jeongs'
console.log(Object.getPrototypeOf(me)) // constructor

Person.prototype을 출력한 결과 getName 메서드가 존재하지 않고,
인스턴스의 메서드로 호출된 것을 확인할 수 있다.
따라서 인스턴스의 메서드를 정의하고 싶을 땐 클래스 필드에 함수를 할당하고,
클래스 몸체에서 메서드 축약 표현을 통해 메서드를 정의할 경우 프로토타입의 메서드가 된다.
마지막으로 클래스 자체에 메서드를 정의하고 싶을 땐 정적 메서드 키워드 static을 활용해 정의한다.

클래스 필드에 관한 토론

클래스 필드 사용 목적 추론

  • Java에서 가진 클래스 기능 중하나인 private, static 프로퍼티 기능을 사용하기 위해서 추가되었다고 볼수 있다.
  • private을 의미하는 #prop 은, constructor 메소드 내부에서 선언되어질수 없다. 이를 활용하려면 반드시 클래스 필드에서의 선언이 필요함. static도 마찬가지이다.
  • 이때를 제외하면, constructor가 가지는 강점인 프로퍼티의 동적할당을 사용하지 못하는점에서, 큰 활용 가능성을 가지지 못함
  • 그리고 아직 공식적으로 추가된 기술이 아니다. ( 크롬과 NodeJS 12v 이상에선 사용 가능 )

super

  1. 서브클래스에서 constructor를 생략하지 않는 경우 서브클래스의 constructor에서는 반드시 super를 호출해야 한다.
class Base {
  constructor(a, b) { // 인스턴스 초기화
    this.a = a
    this.b = b
  }
}

class Derived extends Base {
  constructor() {
    super()
    console.log('super') //
  }
}

const derived = new Derived(1, 2)
console.log(derived) // Derived { a: undefined, b: undefined }

서브클래스의 constructor를 호출 시 super에 아무것도 전달하지 않으면 수퍼 클래스에 인수를 전달하지 못하므로 undefined가 출력되는 것을 확인했다.

class Base {
  constructor(a, b) { // 인스턴스 초기화
    this.a = a
    this.b = b
  }
}

class Derived extends Base {
  constructor(...args) {
    super(...args)
    console.log('super') //
  }
}

const derived = new Derived(1, 2)
console.log(derived) // Derived { a: 1, b: 2 }

new 키워드로 Derived 클래스를 호출할 때의 인수를 ...args를 통해 인수로 받고 super의 인자로 넣어줬을 때야 비로소 상속받아 출력결과로 나타낼 수 있었다.

왜 서브클래스에서 super를 직접 표기해서 사용해야 할까? 왜 자동으로 상속시켜주지 않을까?

만약 자동으로 슈퍼클래스의 프로퍼티를 상속 받아서 입력 받을 파라미터들이 추가됐다면, 서브클래스에서 전달 받는 인수들이 어떤 파라미터에 들어가야 하는지 그 순서를 보장할 수 있는 방법이 마땅치 않아서 그런 것 아닐까?

만약 key-value 형태인 객체를 인수로 전달받는다면 슈퍼클래스에서 어떤 순서로 파라미터를 가져오더라도 key 값을 통해서 매칭시키기 때문에 순서가 섞일 일이 없다. 마치 리액트에서 prop을 '객체' 형태로 넘겨주듯이..!

그래서 클래스의 인스턴스를 생성할 때 객체 형태로만 값을 받았다면 자동으로 상속시켜주는 기능이 구현될 수도 있지 않았을까?

[[HomeObject]]

super 키워드를 통해 수퍼 클래스를 참조할 경우 내부메서드 [[HomeObject]]으로 메서드 자신을 바인딩하고 있는 객체의 프로토타입을 찾을 수 있다.

class Base {
  constructor(name) {
    this.name = name
  }

  sayHi() {
    return `sayHi!`
  }
}

class Derived extends Base {
  sayHi() {
    return `${super.sayHi()}`
  }
}

const derived = new Derived('Jeongs')
console.log(derived.sayHi()) // 'sayHi!'

예를 들어 서브 클래스에서 super 참조로 sayHi를 호출할 경우를 예로 들어보자.
super를 참조하고 있는 메서드 sayHi의 [[HomeObject]]가 자신을 바인딩하고 있는 객체, 즉 Derived.prototype 의 prototype인 Base.prototype을 가리키게 된다.


Derived.prototype에 왜 Base가 적혀있는 걸까?
constructor가 Derived 클래스를 가리키고 있는 걸로 봐선 맞는데...

모든 프로토타입의 프로토타입은 Object 이므로 프로토타입 체인을 따라가기 위해선 proto 나 Object.getPrototypeOf로 확인해보았다.

Base.prototype의 프로토타입 또한 Object.prototype으로 잘 출력되었다.

Base의 의미가 궁금하다 나중에 스터디원들과 논의해봐야겠다!

Derived.prototype에 왜 상위 프로토타입의 constructor인 Base의 이름이 찍힐까?

여러가지 추론을 해본 결과 마땅한 의견이 나오지 않고 Node.js 환경과 브라우저 환경에서 나오는 출력 결과가 달라 단순히 실수일 거라고 결론이 나왔는데 영 찝찝하다.
(Node.js 환경에서는 원래의 Derived.prototype이 가리켜야 하는 Derived의 이름이 잘 출력되었다.)

profile
💪🏻 아는 걸 설명할 줄 아는 개발자 되기

0개의 댓글