ES6에서 super를 사용하지 않고 클래스를 확장하는 방법은 무엇입니까?


98

super부모 클래스를 호출하는 메서드를 호출 하지 않고 ES6에서 클래스를 확장 할 수 있습니까?

편집 : 질문이 오해의 소지가있을 수 있습니다. 우리가 전화해야하는 표준입니까, super()아니면 뭔가 놓치고 있습니까?

예를 들면 :

class Character {
   constructor(){
      console.log('invoke character');
   }
}

class Hero extends Character{
  constructor(){
      super(); // exception thrown here when not called
      console.log('invoke hero');
  }
}

var hero = new Hero();

super()파생 클래스를 호출하지 않을 때 범위 문제가 발생합니다->this is not defined

나는 v2.3.0에서 iojs --harmony로 이것을 실행하고 있습니다.


범위 문제는 무엇을 의미합니까? 예외가 발생합니까 (그리고 어디서)?
Amit

super ()를 호출하지 않고 호출 할 때 파생 클래스에서 기대치를 얻습니다. 나는 그것이 더 명확 :)하게 내 질문에 편집
xhallix

어떤 환경에서 실행하고 있습니까?
Amit

6
다른 클래스를 확장하면 생성자가 먼저 super ()를 호출해야합니다.
Jonathan de M.

@JonathandeM. 고마워요, 그럼이게 미래의 방식일까요?
xhallix

답변:


147

ES2015 (ES6) 수업의 규칙은 기본적으로 다음과 같습니다.

  1. 자식 클래스 생성자에서 thissuper호출 할 때까지 사용할 수 없습니다 .
  2. ES6 클래스 생성자 super는 하위 클래스 인 경우 호출 해야합니다. 그렇지 않으면 초기화되지 않은 개체를 대신하기 위해 명시 적으로 일부 개체를 반환해야합니다.

이것은 ES2015 사양의 두 가지 중요한 섹션으로 나뉩니다.

섹션 8.1.1.3.4this기능에 무엇이 있는지 결정하는 논리를 정의합니다 . 클래스의 중요한 부분 this"uninitialized"상태에있을 수 있으며이 상태에서 사용하려고 this하면 예외가 발생한다는 것입니다.

9.2.2 , [[Construct]],를 통해 호출되는 함수의 동작을 정의하는 new또는 super. 기본 클래스 생성자를 호출 할 때의 this8 단계에서 초기화 [[Construct]]되지만 다른 모든 경우 this에는 초기화되지 않습니다. 생성이 끝날 때가 GetThisBinding호출되므로 super아직 호출되지 않았거나 (초기화 됨 this) 명시 적 교체 객체가 반환되지 않은 경우 생성자 호출의 마지막 줄에서 예외가 발생합니다.


1
호출하지 않고 클래스에서 상속하는 방법을 제안 할 수 super()있습니까?
Bergi

4
ES6 super()에서 생성자 를 호출하지 않고도 클래스에서 상속 할 수 있다고 생각하십니까 ?
Bergi

8
수정 해주셔서 감사합니다. 바로 지금 있습니다. return Object.create(new.target.prototype, …)슈퍼 생성자를 호출 하지 않도록 할 수 있습니다 .
Bergi


1
생성자가 생략되면 어떤 일이 발생하는지 추가해야합니다. 기본 생성자는 MDN 에 따라 사용됩니다 .
kleinfreund

11

복수 응답하고 진술 코멘트가 있었다 super 반드시 첫 번째 줄의 내부에 constructor. 그것은 단순히 잘못된 것입니다. @loganfsmyth 답변에는 요구 사항에 대한 필수 참조가 있지만 다음과 같이 요약됩니다.

상속 ( extends) 생성자 super 사용 되지 않더라도 사용 this하기 전과 반환하기 전에 호출 해야합니다.this

을 (를 this) 호출하기 전에 (를 사용하지 않고 ) 명령문을 갖는 것이 합리적 일 수있는 이유를 보려면 아래 부분 (Chrome에서 작동 ...)을 참조하십시오 super.

'use strict';
var id = 1;
function idgen() {
  return 'ID:' + id++;
}

class Base {
  constructor(id) {
    this.id = id;
  }

  toString() { return JSON.stringify(this); }
}

class Derived1 extends Base {
  constructor() {
    var anID = idgen() + ':Derived1';
    super(anID);
    this.derivedProp = this.baseProp * 2;
  }
}

alert(new Derived1());


2
"See fragment below (works in Chrome...)"크롬 개발자 콘솔을 열고 "Run code snippet":을 클릭합니다 Uncaught ReferenceError: this is not defined. 물론 이전에는 생성자 super()에서 메서드를 사용할 수 있지만 이전에는 클래스의 메서드를 사용할 수 없습니다!
marcel

this이전에 사용할 수없는 super()(코드 증명)은 사양과 즉시 관련이 없지만 자바 스크립트 구현과 관련이 있습니다. 그래서 'this'보다 먼저 'super'를 불러야합니다.
marcel

@marcel-나는 우리가 많은 혼란을 겪었다 고 생각합니다. 나는을 (를) 사용하기 전에 STATEMENTS 를 사용 하는 것이 합법적이며을 (를) 호출하기 전에 super사용하는 것은 불법이라고 말하고있었습니다 . 우리는 둘 다 맞습니다. 서로를 이해하지 못했습니다. :-) (그리고 그 예외는 합법적이지 않은 것을 보여주기 위해 의도적 인 것이 었습니다. 심지어 속성 이름을 'WillFail'로 지정했습니다.)thissuper
Amit

9

새로운 es6 클래스 구문은 프로토 타입이있는 "이전"es5 "클래스"에 대한 다른 표기법 일뿐입니다. 따라서 프로토 타입 (기본 클래스)을 설정하지 않고 특정 클래스를 인스턴스화 할 수 없습니다.

그것은 치즈를 만들지 않고 샌드위치에 넣는 것과 같습니다. 또한 샌드위치 만들기 전에 치즈 넣을 수 없으니 ...

... this슈퍼 클래스를 호출하기 전에 키워드를 사용 하는 super()것도 허용되지 않습니다.

// valid: Add cheese after making the sandwich
class CheeseSandwich extend Sandwich {
    constructor() {
        super();
        this.supplement = "Cheese";
    }
}

// invalid: Add cheese before making sandwich
class CheeseSandwich extend Sandwich {
    constructor() {
        this.supplement = "Cheese";
        super();
    }
}

// invalid: Add cheese without making sandwich
class CheeseSandwich extend Sandwich {
    constructor() {
        this.supplement = "Cheese";
    }
}

기본 클래스에 대한 생성자를 지정하지 않으면 다음 정의가 사용됩니다.

constructor() {}

파생 클래스의 경우 다음 기본 생성자가 사용됩니다.

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

편집 :이 발견 developer.mozilla.org:

When used in a constructor, the super keyword appears alone and must be used before the this keyword can be used.

출처


그래서 내가 당신을 올바르게 이해하면 super ()를 호출하지 않을 때 내 클래스 Hero에서 Character 클래스의 메서드를 사용할 수 없습니다. 그러나 이것은 기본 클래스에서 메서드를 호출 할 수 있기 때문에 완전히 정확하지 않은 것 같습니다. 그래서 나는 생성자 호출 할 때 난 그냥 슈퍼 필요 추측
xhallix

1. OP에서는 this전혀 사용하지 않습니다. 2. JS는 샌드위치가 아닙니다. ES5 this에서는 원하는 다른 함수를 호출하기 전에도 항상를 사용할 수 있습니다 (보충 속성을 정의 할 수도 있고 정의하지 않을 수도 있음)
Amit

@amit 1. 이제 나도 사용할 수 없습니까 this?! 2. 내 JS 클래스는 샌드위치를 ​​나타내며 ES6에서는 항상 this. 나는 단지 es6 클래스 (은유로)를 설명하려고 노력하고 있으며 아무도 그러한 파괴적이고 불필요한 주석이 필요하지 않습니다.
마르셀

@marcel 냉소주의에 대해 사과드립니다.하지만 : 1. OP에 존재하지 않는 새로운 문제를 소개하는 것이 었습니다. 2는 귀하의 주장이 잘못되었음을 알리는 것입니다 (여전히
Amit

@marcel - 내 대답을 참조하십시오
아 미트

4

실제로 이것에 대한 간단한 방법이 있기 때문에 여기에 대한 답변이 나를 만족시키지 못하기 때문에이 솔루션을 게시하도록 등록했습니다. 슈퍼 생성자 만 사용하는 동안 하위 메서드에서 논리를 덮어 쓰고 생성자 인수를 여기에 전달하도록 클래스 생성 패턴을 조정합니다 .

에서와 같이 하위 클래스 자체에 생성자를 만들지 않고 해당 하위 클래스에서 재정의 된 메서드에 대한 참조 만 만듭니다.

즉, 사용자에게 적용되는 생성자 기능에서 자유 로워지고 일반 메서드를 사용 하지 않습니다.이 메서드는 재정의 될 수 있으며 super (완전히 호출하려는 경우)를 선택할 때 super ()를 적용하지 않습니다. 선택 사항) 예 :

super.ObjectConstructor(...)

class Observable {
  constructor() {
    return this.ObjectConstructor(arguments);
  }

  ObjectConstructor(defaultValue, options) {
    this.obj = { type: "Observable" };
    console.log("Observable ObjectConstructor called with arguments: ", arguments);
    console.log("obj is:", this.obj);
    return this.obj;
  }
}

class ArrayObservable extends Observable {
  ObjectConstructor(defaultValue, options, someMoreOptions) {
    this.obj = { type: "ArrayObservable" };
    console.log("ArrayObservable ObjectConstructor called with arguments: ", arguments);
    console.log("obj is:", this.obj);
    return this.obj;
  }
}

class DomainObservable extends ArrayObservable {
  ObjectConstructor(defaultValue, domainName, options, dependent1, dependent2) {
    this.obj = super.ObjectConstructor(defaultValue, options);
    console.log("DomainObservable ObjectConstructor called with arguments: ", arguments);
    console.log("obj is:", this.obj);
    return this.obj;
  }
}

var myBasicObservable = new Observable("Basic Value", "Basic Options");
var myArrayObservable = new ArrayObservable("Array Value", "Array Options", "Some More Array Options");
var myDomainObservable = new DomainObservable("Domain Value", "Domain Name", "Domain Options", "Dependency A", "Depenency B");

건배!


2
나는이 매우 깊은 대답 같은 느낌이 있지만 복잡하고 무시가 이물 .. 이것상의 "나는 다섯 것 같은 설명"필요
swyx을

@swyx : 마법은 생성자 내부에 있으며, 여기서 'this'는 생성하는 객체 유형에 따라 다른 유형의 객체를 나타냅니다. 예를 들어 새 DomainObservable을 생성하는 경우 this.ObjectConstructor는 다른 메서드 즉 DomainObserveable.ObjectConstructor를 참조합니다. 새로운 ArrayObservable을 생성하는 경우 this.ObjectConstructor는 ArrayObservable.ObjectConstructor를 참조합니다.
cobberboy

내 대답을 참조하십시오, 나는 훨씬 더 간단 예를 게시
마이클 루이스

@swyx에 전적으로 동의합니다. 이 대답은 너무 많은 일을하고 있습니다. 나는 그것을 훑어 보았을 뿐이고 이미 피곤합니다. 나는 "나는 ... 오이야 정말 오줌을 가지고있는 것처럼 설명"같은 기분
SPB

4

서브 클래스에서 생성자를 모두 생략하면 서브 클래스에서 super ()를 생략 할 수 있습니다. '숨겨진'기본 생성자가 하위 클래스에 자동으로 포함됩니다. 그러나 하위 클래스에 생성자를 포함하는 경우 해당 생성자에서 super ()를 호출해야합니다.

class A{
   constructor(){
      this.name = 'hello';   
   }
}
class B extends A{
   constructor(){
      // console.log(this.name); // ReferenceError
      super();
      console.log(this.name);
   }
}
class C extends B{}  // see? no super(). no constructor()

var x = new B; // hello
var y = new C; // hello

읽기 자세한 내용은.


2

justyourimage의 대답은 가장 쉬운 방법이지만 그의 예는 약간 부풀어 있습니다. 다음은 일반 버전입니다.

class Base {
    constructor(){
        return this._constructor(...arguments);
    }

    _constructor(){
        // just use this as the constructor, no super() restrictions
    }
}

class Ext extends Base {
    _constructor(){ // _constructor is automatically called, like the real constructor
        this.is = "easy"; // no need to call super();
    }
}

실제를 확장하지 말고 인스턴스화 논리에 constructor()가짜 _constructor()를 사용하십시오 .

이 솔루션은 모든 인스턴스화에 대해 추가 메서드를 실행해야하기 때문에 디버깅을 성가 시게 만듭니다.


예, 이것이 가장 쉬운 방법이고 가장 명확한 대답입니다. 제가 묻는 질문은 ... "왜, 신, 왜!?"... super ()가 자동이 아닌 매우 타당한 이유가 있습니다. .. 특정 매개 변수를 기본 클래스에 전달하여 기본 클래스를 인스턴스화하기 전에 처리 / 논리 / 사고를 수행 할 수 있습니다.
LFLFM

1

시험:

class Character {
   constructor(){
     if(Object.getPrototypeOf(this) === Character.prototype){
       console.log('invoke character');
     }
   }
}


class Hero extends Character{
  constructor(){
      super(); // throws exception when not called
      console.log('invoke hero');
  }
}
var hero = new Hero();

console.log('now let\'s invoke Character');
var char = new Character();

Demo


이 예제에서는 super ()도 사용하고 그대로두면 예외가 발생합니다. 그래서 나는이 슈퍼 ()이 경우 전화 생략 할 수 없습니다 생각
xhallix

나는 당신의 목표가 부모 생성자를 실행하는 것이 아니라고 생각했는데, 이것이이 코드가하는 일입니다. 확장 할 때 슈퍼를 제거 할 수 없습니다.
조나단 드 M.

미안 beeing는 오해의 소지가 : 아니, 난 그냥 다른 언어로 우리는 파생 클래스의 생성자를 호출 슈퍼 메소드를 호출 할 필요가 없기 때문에 슈퍼 () 나는 구문에 대해 궁금해하기 때문에 사용하기 정말 필요한 경우 알고 싶어위한
xhallix

1
에 따르면 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... , A constructor *can* use the super keyword to call the constructor of a parent class.그래서 내가 말을 ES6 릴리스에 대한 대기
조나단 드 M.

3
"나는 ES6 릴리스에 대한 대기를 말할 수 있도록"--- 이미, 이미 출시 된 ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf은
zerkms

1

다음과 같은 OOP 개념을 개발하려는 경우 OODK-JS 를 사용하는 것이 좋습니다 .

OODK(function($, _){

var Character  = $.class(function ($, µ, _){

   $.public(function __initialize(){
      $.log('invoke character');
   });
});

var Hero = $.extends(Character).class(function ($, µ, _){

  $.public(function __initialize(){
      $.super.__initialize();
      $.log('invoke hero');
  });
});

var hero = $.new(Hero);
});

0

간단한 해결책 : 설명 할 필요가 없다고 생각합니다.

class ParentClass() {
    constructor(skipConstructor = false) { // default value is false
        if(skipConstructor) return;
        // code here only gets executed when 'super()' is called with false
    }
}
class SubClass extends ParentClass {
    constructor() {
        super(true) // true for skipping ParentClass's constructor.
        // code
    }
}

위의 모든 상용구 코드가 왜 그런지 잘 모르겠으며이 접근 방식에 부작용이 있는지 정확히 모르겠습니다. 문제없이 저에게 효과적입니다.
MyUserInStackOverflow

1
한 가지 문제 : 당신이 다시 서브 클래스를 확장 할 경우, 각 하위 클래스 '생성자에 skipConstructor 기능을 구축해야합니다
마이클 루이스

0

@Bergi가 언급 new.target.prototype했지만 전혀 호출하지 않고도 액세스 할 수 있음을 증명하는 구체적인 예제를 찾고있었습니다 this(또는 클라이언트 코드가 생성하는 객체에 대한 new참조, 아래 참조) super().

Talk는 저렴합니다. 코드를 보여주세요. 여기에 예가 있습니다.

class A { // Parent
    constructor() {
        this.a = 123;
    }

    parentMethod() {
        console.log("parentMethod()");
    }
}

class B extends A { // Child
    constructor() {
        var obj = Object.create(new.target.prototype)
        // You can interact with obj, which is effectively your `this` here, before returning
        // it to the caller.
        return obj;
    }

    childMethod(obj) {
        console.log('childMethod()');
        console.log('this === obj ?', this === obj)
        console.log('obj instanceof A ?', obj instanceof A);
        console.log('obj instanceof B ?',  obj instanceof B);
    }
}

b = new B()
b.parentMethod()
b.childMethod(b)

다음을 출력합니다.

parentMethod()
childMethod()
this === obj ? true
obj instanceof A ? true
obj instanceof B ? true

당신은 우리가 효과적으로 유형의 객체 생성되는 것을 볼 수 있도록 B또한 형식의 개체입니다 (하위 클래스) A(부모 클래스)과 내 childMethod()아이의 B우리가 한 this객체를 가리키는 obj우리가 B의에서 만들어 constructorObject.create(new.target.prototype).

그리고이 모든 것이 전혀 신경 쓰지 않고 super있습니다.

이것은 JS constructor에서 클라이언트 코드가 new.

이것이 누군가를 돕기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.