파생 클래스 속성 값이 기본 클래스 생성자에 표시되지 않는 이유는 무엇입니까?
코드를 작성했습니다.
class Base {
// Default value
myColor = 'blue';
constructor() {
console.log(this.myColor);
}
}
class Derived extends Base {
myColor = 'red';
}
// Prints "blue", expected "red"
const x = new Derived();
파생된 클래스 필드 이니셜라이저가 기본 클래스 생성자보다 먼저 실행될 것으로 예상했습니다.대신, 파생 클래스는 다음을 변경하지 않습니다.myColor
기본 클래스 생성자가 실행된 후까지 속성이 있으므로 생성자에서 잘못된 값을 관찰합니다.
이거 벌레야?뭐가 잘못됐나요?왜 이런 일이 일어날까요?제가 대신 무엇을 해야 할까요?
버그 아님
먼저, 이것은 TypeScript, Babel 또는 JS 런타임의 버그가 아닙니다.
이렇게 해야 하는 이유
첫 번째 후속 조치는 "이것을 올바르게 수행하지 않는 이유는 무엇입니까!?"입니다.TypeScript가 내보내는 구체적인 사례를 살펴보겠습니다.실제 답변은 클래스 코드를 내보내는 ECMA스크립트 버전에 따라 달라집니다.
하위 레벨 배출: ES3/ES5
ES3 또는 ES5용 TypeScript에서 출력된 코드를 살펴보겠습니다.가독성을 위해 이를 간략화하고 주석을 달았습니다.
var Base = (function () {
function Base() {
// BASE CLASS PROPERTY INITIALIZERS
this.myColor = 'blue';
console.log(this.myColor);
}
return Base;
}());
var Derived = (function (_super) {
__extends(Derived, _super);
function Derived() {
// RUN THE BASE CLASS CTOR
_super();
// DERIVED CLASS PROPERTY INITIALIZERS
this.myColor = 'red';
// Code in the derived class ctor body would appear here
}
return Derived;
}(Base));
기본 클래스 방출은 논란의 여지 없이 정확합니다. 필드가 초기화된 다음 생성자 본문이 실행됩니다.생성자 본문을 실행하기 전에 필드를 초기화하는 것은 생성자 이후까지 필드 값을 볼 수 없다는 것을 의미하며, 이는 누구나 원하는 것이 아닙니다.
파생 클래스 방출이 올바릅니까?
아니요, 당신은 주문을 교환해야 합니다.
많은 사람들은 파생 클래스 방출이 다음과 같이 되어야 한다고 주장합니다.
// DERIVED CLASS PROPERTY INITIALIZERS
this.myColor = 'red';
// RUN THE BASE CLASS CTOR
_super();
이는 여러 가지 이유로 인해 매우 잘못된 것입니다.
- ES6에는 해당 동작이 없습니다(다음 섹션 참조).
- »
'red'
위해서myColor
값 '로 . - 파생된 클래스 필드 이니셜라이저는 기본 클래스 초기화에 종속된 기본 클래스 메서드를 호출할 수 있습니다.
마지막으로 다음 코드를 고려합니다.
class Base {
thing = 'ok';
getThing() { return this.thing; }
}
class Derived extends Base {
something = this.getThing();
}
이니셜라이저보다 되었다면,Derived#something
항상그것은럴일 것입니다.undefined
분명히 그래야 할 때에.'ok'
.
아니요, 당신은 타임머신을 사용해야 합니다.
많은 다른 사람들은 모호한 다른 것이 행해져야 한다고 주장할 것입니다.Base
을 알고 있습니다Derived
필드 이니셜라이저가 있습니다.
실행할 코드 전체를 아는 것에 의존하는 예제 솔루션을 작성할 수 있습니다.그러나 TypeScript / Babel / 등은 이것이 존재한다고 보장할 수 없습니다.를 들면, 들면를예,Base
구현을 확인할 수 없는 별도의 파일에 있을 수 있습니다.
하향 배출: ES6
만약 당신이 이것을 아직 알지 못했다면, 이제 배울 시간입니다: 수업은 TypeScript 기능이 아닙니다.그들은 ES6의 일부이고 정의된 의미론을 가지고 있습니다.그러나 ES6 클래스는 필드 이니셜라이저를 지원하지 않으므로 ES6 호환 코드로 변환됩니다.다음과 같이 표시됩니다.
class Base {
constructor() {
// Default value
this.myColor = 'blue';
console.log(this.myColor);
}
}
class Derived extends Base {
constructor() {
super(...arguments);
this.myColor = 'red';
}
}
대신에
super(...arguments);
this.myColor = 'red';
이거 먹을까요?
this.myColor = 'red';
super(...arguments);
아니요, 효과가 없기 때문입니다.을 참조하는 것은 불법입니다.this
호출하기 전에super
파생 클래스에서단순히 이런 식으로는 작동할 수 없습니다.
ES7+: 공용 필드
JavaScript를 제어하는 TC39 위원회는 향후 버전의 언어에 필드 이니셜라이저를 추가하는 것을 조사하고 있습니다.
GitHub에서 이에 대해 읽거나 초기화 순서에 대한 특정 문제를 읽을 수 있습니다.
OOP 새로 고침: 생성자의 가상 동작
모든 OOP 언어에는 일반적인 지침이 있으며, 일부는 명시적으로 시행되고 일부는 암묵적으로 관례에 따라 시행됩니다.
생성자에서 가상 메서드 호출 안 함
예:
- C# 생성자의 가상 멤버 호출
- C++ 생성자 내부의 가상 함수 호출
- 생성자의 Python Calling 멤버 함수
- 자바 자바에서 생성자로부터 추상 메소드를 호출해도 괜찮습니까?
자바스크립트에서, 우리는 이 규칙을 조금 확장해야 합니다.
생성자에서 가상 동작 관찰 안 함
그리고.
클래스 속성 초기화가 가상으로 카운트됨
솔루션
표준 솔루션은 필드 초기화를 생성자 매개 변수로 변환하는 것입니다.
class Base {
myColor: string;
constructor(color: string = "blue") {
this.myColor = color;
console.log(this.myColor);
}
}
class Derived extends Base {
constructor() {
super("red");
}
}
// Prints "red" as expected
const x = new Derived();
사용할 수도 있습니다.init
패턴에서 가상 행동을 관찰하지 않고 파생된 작업을 수행하지 않도록 주의해야 하지만 패턴init
기본 클래스의 완전한 초기화가 필요한 메서드:
class Base {
myColor: string;
constructor() {
this.init();
console.log(this.myColor);
}
init() {
this.myColor = "blue";
}
}
class Derived extends Base {
init() {
super.init();
this.myColor = "red";
}
}
// Prints "red" as expected
const x = new Derived();
저는 이것이 사실, 벌레라고 정중하게 주장하고 싶습니다.
예기치 않은 작업을 수행함으로써 일반적인 클래스 확장 사용 사례를 위반하는 원하지 않는 동작입니다.사용 사례를 지원하는 초기화 순서는 다음과 같습니다.
Base property initializers
Derived property initializers
Base constructor
Derived constructor
문제 / 해결 방법
유형 스크립트 컴파일러가 현재 생성자에서 속성 초기화를 내보냅니다.
여기서 해결책은 생성자 함수 호출에서 속성 초기화를 분리하는 것입니다.C#은 파생된 속성 뒤에 기본 속성에 포함되지만 이 역시 반직관적입니다.이 작업은 도우미 클래스를 내보내 파생 클래스가 기본 클래스를 임의 순서로 초기화할 수 있도록 하여 수행할 수 있습니다.
class _Base {
ctor() {
console.log('base ctor color: ', this.myColor);
}
initProps() {
this.myColor = 'blue';
}
}
class _Derived extends _Base {
constructor() {
super();
}
ctor() {
super.ctor();
console.log('derived ctor color: ', this.myColor);
}
initProps() {
super.initProps();
this.myColor = 'red';
}
}
class Base {
constructor() {
const _class = new _Base();
_class.initProps();
_class.ctor();
return _class;
}
}
class Derived {
constructor() {
const _class = new _Derived();
_class.initProps();
_class.ctor();
return _class;
}
}
// Prints:
// "base ctor color: red"
// "derived ctor color: red"
const d = new Derived();
파생 클래스 속성을 사용하기 때문에 기본 생성자가 손상되지 않습니까?
기본 생성자에서 중단되는 모든 논리는 파생 클래스에서 재정의되는 메서드로 이동할 수 있습니다.파생 메서드는 기본 생성자를 호출하기 전에 초기화되므로 올바르게 작동합니다.예:
class Base {
protected numThings = 5;
constructor() {
console.log('math result: ', this.doMath())
}
protected doMath() {
return 10/this.numThings;
}
}
class Derived extends Base {
// Overrides. Would cause divide by 0 in base if we weren't overriding doMath
protected numThings = 0;
protected doMath() {
return 100 + this.numThings;
}
}
// Should print "math result: 100"
const x = new Derived();
언급URL : https://stackoverflow.com/questions/43595943/why-are-derived-class-property-values-not-seen-in-the-base-class-constructor
'bestsource' 카테고리의 다른 글
막대 그림에 대해 R의 x축 레이블 회전 (0) | 2023.06.18 |
---|---|
Firestore 하위 수집 대 어레이 (0) | 2023.06.18 |
여러 패턴이 있는 문자 벡터를 사용한 grep (0) | 2023.06.18 |
Wordpress 이미지 업로드에 연결 (0) | 2023.06.18 |
당신은 TypeScript에서 기능을 확장할 수 있습니까? (0) | 2023.06.18 |