質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

Q&A

5回答

1501閲覧

JavaScript 継承について 子の値が更新されない

退会済みユーザー

退会済みユーザー

総合スコア0

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

0グッド

2クリップ

投稿2020/09/01 22:36

編集2020/09/01 23:50

js

1class Parent { 2 constructor() { 3 this.name = 'parent' 4 this.init() 5 } 6 7 init() { 8 this.p1 = 'p1' 9 } 10} 11 12class Child1 extends Parent { 13 constructor() { 14 super() 15 this.name = 'child' 16 } 17 18 init() { 19 super.init() 20 console.log(this.name) 21 this.p2 = 'p2' 22 } 23} 24 25class Child2 extends Parent { 26 constructor() { 27 super() 28 this.name = 'child' 29 this.p2 = 'a' 30 } 31 32 init() { 33 super.init() 34 console.log(this.name) 35 this.p2 = 'p2' 36 } 37} 38 39const c1 = new Child1() 40const c2 = new Child2()

このときc1.p2には'p2'と予想通りの値が格納されているのですが、c2.p2には'a'が格納されています。Child2init()が呼ばれているため'p2'という値が格納されるだろうと思っていましたが、なぜこのような違いが生まれるのでしょうか。

また、p2をコンストラクタで初期化し、さらにinit()関数でp2の値を変更するにはどうしたら良いでしょうか。

問題の背景

もともとのコードではChild1のように、constructor()p2を初期化していませんが、tsがjsにトランスパイルされることにより自動的にconstructor()の中に

js

1this.p2 = void 0

と挿入されるため、Child2のような状況に陥ってしまいました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

bsdfan

2020/09/01 23:27

Child2のconstructorの最後の this.p2 = 'a' は、必要なんですか?
退会済みユーザー

退会済みユーザー

2020/09/01 23:47

実はtypescriptを用いてプログラムを書いています。 もともとのコードではChild1のようにconstructorでp2を初期化していませんが、tsがjsに変換されることにより自動的にconstructorの中に this.p2 = void 0 と挿入されるため、Child2のような状況になってしまったというのが背景です。
bsdfan

2020/09/01 23:59

typescriptはあまり詳しくないのですが、たぶん元のtypescriptのコードも載せたほうが、回答付きやすいと思います。
miyabi_takatsuk

2020/09/02 01:22 編集

TypeScriptを使用している理由が定かでないためなんとも言えませんが、 JavaScriptだけでも可能な構文なので、 そもそもTypeScript使わなければよいのではないでしょうか? んで、Child2のcostructorでのp2への代入を辞めるとか。
退会済みユーザー

退会済みユーザー

2020/09/02 01:37 編集

TypeScriptを使わなければよいという回答は的外れ
miyabi_takatsuk

2020/09/02 02:52

ここは回答ではなく、質問修正依頼です。
quickquip

2020/09/02 05:28

Typescriptのコードとトランスパイルに関すること(tsconfig.jsonあたり)が隠されていると解決できなさそうですね。
miyabi_takatsuk

2020/09/02 05:54

> Typescriptのコードとトランスパイルに関すること(tsconfig.jsonあたり)が隠されていると解決できなさそうですね。 なるほど、トランスパイル設定ですか・・・。 質問者さん、 this.p2 = void 0 が発生しないようなトランスパイル設定を行うという方向性の方がいいのではないでしょうか? そもそもそれが可能かどうかはわかりませんが・・・。
Zuishin

2020/09/02 07:25

Child2 に this.p2 の代入が二つあるので片方だけにすればいいだけだと思いますが、何が問題なんでしょうか。
guest

回答5

0

順番を考えてみてください

javascript

1 constructor() { 2 super() 3 this.name = 'child' 4 this.p2 = 'a' 5 }

constructorでsuper()を呼ぶと、

javascript

1 this.name = 'parent' 2 this.init()

が走ります。
initの中でthis.p2 = "p2"としているのでその値が入ります
super()の後で、

javascript

1 this.p2 = 'a'

としているのですから、当然c2.p2には'a'が入ります。

投稿2020/09/01 23:57

Hogeike

総合スコア293

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

自動的にconstructor()の中に this.p2 = void 0 と挿入される

this.p2 = void 0 が挿入されない場合、[[Prototype]] から参照される親オブジェクトの p2 プロパティを探そうとします(プロトタイプサーチ)。

インスタンスが直接 p2 プロパティを有しているように値void 0undefined)を定義したと考えるべきです。

おそらくですが、トランスパイラがプロトタイプサーチに係るパフォーマンスを優先した結果だと思います(誤差として見逃しがちな内容です)。

後から明確な値 p2 が代入されるなら問題ありませんし、Child2 の constructor() 内にある this.p2 = 'a' は不要です。

投稿2020/09/03 08:36

編集2020/09/03 08:42
AkitoshiManabe

総合スコア5432

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

コンストラクタで仮想関数っぽいものを呼び出すのが悪いんだと思いますが(未初期化のクラスを使うので)、それを是とするなら、いっそのことコンストラクタの実装を別の名前に(ここでは_constructor())してみました。

良い悪いで言えば「悪い」ですが。。。質問者さんの意図には適合します。
誤解を生むので本当はコンストラクタを連想しない名前がいいですけど。

javascript

1class Parent { 2 constructor() { 3 this._constructor() 4 this.init() 5 } 6 7 _constructor() { 8 name = 'parent' 9 } 10 11 init() { 12 this.p1 = 'p1' 13 } 14} 15 16class Child1 extends Parent { 17 _constructor() { 18 super._constructor() 19 this.name = 'child' 20 } 21 22 init() { 23 super.init() 24 console.log(this.name) 25 this.p2 = 'p2' 26 } 27} 28 29class Child2 extends Parent { 30 _constructor() { 31 super._constructor() 32 this.name = 'child' 33 this.p2 = 'a' 34 } 35 36 init() { 37 super.init() 38 console.log(this.name) 39 this.p2 = 'p2' 40 } 41} 42 43const c1 = new Child1() 44const c2 = new Child2()

投稿2020/09/02 12:44

dameo

総合スコア943

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/09/02 14:52 編集

多くのプロパティをコンストラクタの中である値に初期化し、さらにその後任意のタイミングで同じような初期化を行いたいという場合を仮に想定します。 この場合、初期化用の関数を作ってそれをコンストラクタ内で呼ぶというよりも、コンストラクタ内と別の場所で全く同じ初期化の処理を2箇所に書くという方が良いのでしょうか。
dameo

2020/09/02 19:31

そういう作りにはしないし、したなら良くないと言わざるを得ません。 ❝初期化が済んでないクラスのメソッドが呼ばれて❞、実際に質問者さんが「意図しない」と思った動作をして、質問するに至ったわけなんですから。 そもそもあまり実装の継承は追いにくくなるので好かれません。対応方法はケースバイケースなので、今みたいな意味の分からないクラスの状態(Parent,Child1,Child2のような役割が明確でない状態)では見えません。
guest

0

Child2のinit()が呼ばれているため'p2'という値が格納されるだろうと思っていましたが

格納はされますが、その後で上書きされます。

また、p2をコンストラクタで初期化し、さらにinit()関数でp2の値を変更するにはどうしたら良いでしょうか。

そもそも論として、コンストラクタと別にinitメソッドを作る意図はどのようなものでしょうか?

投稿2020/09/01 22:51

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/09/01 22:57

> そもそも論として、コンストラクタと別にinitメソッドを作る意図はどのようなものでしょうか? 後にinitを再度クラス内から呼び出して値を初期化させたいのです。
guest

0

どうしても、処理の順番などを変えるのが難しければ、
setTimeoutで処理を遅らせるのも一つ手かと思います。

javascript

1class Child2 extends Parent { 2 constructor() { 3 super() 4 this.name = 'child' 5 this.p2 = 'a' 6 } 7 8 init() { 9 super.init() 10 console.log(this.name) 11 setTimeout(() => { 12 this.p2 = 'p2'; 13 }, 1); 14 15 } 16}

ただし、制御がしづらく、後の実装に支障が出る可能性は否めません。
よって、構築や、処理の順番などで工夫する方がいいかと思います。

投稿2020/09/02 03:02

miyabi_takatsuk

総合スコア9528

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問