「javascript逆引きレシピ」の073pにて、クラス(コンストラクタ)を定義することに関する情報が記載されているのですが、そのクラスを学ぶ際の注意点として、コンストラクタは自動的にthisの指すオブジェクトを返すので返り値はいらないと記述してありました。つまり、インスタンスのもとになる関数は、そのインスタンスを自動的に返すから戻り値はいらないということだと思うのですが、書いてある意味がいまいちわからないのです。
注意を促している箇所で記述されているので、何か大切な理由があると思うのですが、理解できません。
ちなみに、返り値やインスタンスという単語の意味は理解しているつもりですが、このように上記の文章になると何を伝えたかったのか理解できなくなります。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答3件
0
ベストアンサー
仕様書を確認したところ、かなり複雑な動作をするようです。new F(args)
とした場合、最終的にF.[[Construct]](args, F)
が呼び出されるのですが、この[[Construct]]
は内部メソッド(JavaScriptの内部処理だけに使われるメソッド、コードには現れない)であり、他の関数とは異なる特殊な動作をします。
参考: ECMAScript 2015 9.2.2 Construct
ざくっと動作の概要を説明すると、Fのオブジェクトを作成してそれをthis
にbindしたF()を評価した後に、
- return文で終わっているとき
1-1. returnの結果がObject型であれば、その結果を返す。
1-2. そうではなく、Fが基本型(派生型では無い)であれば、作成したFのオブジェクトを返す。
1-3. そうではなく、returnの結果がundefinedでなければ、TypeError例外を発生させる。
2. 1でない、または、1-1〜1-3のどれでもなければ、作成したFのオブジェクトを返す。
という動作をします(厳密ではありませんし、詳しいところまで見てないので間違いがあるかも知れません)。つまり、Fのコンストラクタがreturn {};
で終わっていれば、new F(args)
は{}
返しますが、Fが基本型でreturn 0;
などで終わっていれば、0ではなくFのオブジェクトが返します。そもそもreturn文がない、または、undefined
を返すのであれば、Fのオブジェクトを返します。
なお派生(derived)型以外の全てのクラス(関数)は全て基本(base)型です。派生型はECMAScript 2015のclass構文でextends
を使った場合とジェネレーター以外は無いようです。functionのみを使ったECMAScript 5までの書き方(babel等で変換した場合も同様)であれば、TypeError例外が発生する事は無いので、気にしなくても良いようです。
まとめるとnew
演算子でオブジェクトを生成した場合、返ってくる値は下記の通りです。
- コンストラクタに値を指定したreturn文がなければ、新たに作成されたそのクラスのオブジェクトが返ります。
- コンストラクタに値を指定したreturn文があれば、
2-1. return文で指定した値がObject型であれば、その指定した値が返ります。
2-2. それ以外なら、return文がない場合と同じです。
2-3. ただし、ECMAScript 2015からのclass構文を使っていると例外が発生する場合があります。
法則が複雑で混乱の元なので、値を指定したreturn文は書かない(return文そのものがない、または、return;
※のみ)のがいいでしょう。
※return;
はreturn undefined;
と同じですが、その場合も、必ず作成したオブジェクトが返り、例外も発生しません。
投稿2016/01/05 14:43
編集2016/01/05 14:54総合スコア21739
0
コンストラクター関数が自動的にインスタンスを返すというよりは、関数はthis
に対する操作の役割しか持たずに、戻り値は無視されるといったほうがわかりやすいかもしれません。
注意を促したいのは、この 戻り値が無視される という性質についてではないかと思います。
つまり「無視されるので何かをreturnしてもその値が返される訳ではないですよ」という注意なのではないかと。
ちなみにJavaScriptのコンストラクターを理解する上で、個人的にしっくり来た説明を紹介します。
javascript
1// 普通に初期化 2var instance = new SomeClass("初期化パラメーター"); 3 4// 以下3行で、ほぼ同じ動きをする 5var instance = Object.create(SomeClass.prototype); 6var constructor = SomeClass.bind(instance) 7constructor("初期化パラメーター");
どこかでこんな説明を見た時、あぁ、そういう動きなんだと納得しました。
5~7行目で、5行目はSomeClassのprototypeオブジェクトをプロトタイプとするオブジェクトを作ります。
new した時も、SomeClassのprototypeオブジェクトをプロトタイプとするオブジェクトができるのでこれは同じです。
6行目、SomeClass
をただの関数として実行した時、this
がinstance
を指すようにbind
された関数を作成します。
最後にその関数を実行すると、instance
の状態がnew
した場合と同じになります。
一番下のconstructor(...)
の戻り値は捨てられていますよね。
あくまでinstance
をthis
とした処理をして、instance
の内部状態を初期化する役割しかないということです。
投稿2016/01/05 12:13
総合スコア790
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。