Discussions

Q子オブジェクトから親オブジェクトへの参照

Hoge.someMethod2.innerMethodからHoge.someMethod1を呼びたいのですが、こういったケースだとどういう風にして呼び出すのが一般的なのでしょうか?

Sample Code

AAnswer to: 子オブジェクトから親オブジェクトへの参照

kyo_ago

Hogeからは子オブジェクトが存在するかどうか分からない
(自分が親になっているのかどうか分からない)ので
Hoge内から子オブジェクトに関する記述を行うことはできません。

この場合、Hogeを継承する子オブジェクト(子クラス)を作成し、
その中から上位クラス(.prototype拡張されているクラス)を参照することになります。

JSの場合、オブジェクトの継承関係は.prototypeの継承関係で表され、
オブジェクトの入れ子関係はオブジェクトの継承関係と関係ありません。
(オブジェクトの入れ子関係は他言語で言うところのHashの入れ子関係になります)

fingaholic

ご回答有難うございます。

>Hogeからは子オブジェクトが存在するかどうか分からない
>(自分が親になっているのかどうか分からない)ので
>Hoge内から子オブジェクトに関する記述を行うことはできません。

とありますがコードの1のように、
someMethod2からsomeMethod1を呼ぶような書き方は
通常しないということでしょうか?
ちょっと混乱してきましたので念のためご確認させて頂きます。

当初の質問の意図としてはコードの2のようにネストされた場合のsomeMethodCollection.innerMethodから
someMethod1を参照したい場合の一般的?な使用法を知りたく思っています。
回答のコードだとネストされていなかったので。。。

kyo_ago

けっこう難しい話なので基本的な事項と、質問されている内容に関しての解答を併記します。

・基本的な事項

まず重要な点として、JSにとって以下のような階層構造は
thisとして呼び出す場合に影響するオブジェクトの継承関係とは一切関係がないという問題があります。

var hoge = {
'huga' : {
'foo' : function () {
alert('ここのthisはhogeともhugaとも一切関係ない\n(hoge.hugaとは関係がある)');
}
}
};

基本的にJSで継承関係を表現するのであれば階層構造毎に.prototype継承を行う必要があります。

・質問されている内容に関して

1. 「someMethod2から、someMethod1を呼ぶような書き方は通常しないか?」

通常この方法で呼び出します。
わたしが前回説明したのはサンプルコードとしてなので、通常.prototype拡張しているmethod内から
同じオブジェクト内のmethod, propertyを参照する場合this経由で参照します。

2. 「さらにネストが深くなった場合(innerMethod)、どのように継承してsomeMethod1を参照するのか」

この場合、innerMethodからsomeMethod1を呼び出す簡単な方法はありません。

というのも、この場合fuga.someMethodCollection.innerMethod1のthisはfuga.someMethodCollectionになっており、
fugaとは全く別のオブジェクトになっているためです。
(JSの.はオブジェクトの継承関係ではなく、プロパティの参照をしていることに注意してください)

こう行った場合の解決方法としてはfuga.someMethodCollectionにfugaの参照を持たせて
fuga.someMethodCollection.innerMethod1内からthis.fuga経由で参照のが一般的かもしれません。
(が、これだと循環参照になるので普通はもうちょっと設計的な方法でinnerMethod1からfugaの参照を避けるかもしれません)

uupaa

>Hoge.someMethod2.innerMethodからHoge.someMethod1を呼びたいのですが、
>こういったケースだとどういう風にして呼び出すのが一般的なのでしょうか?

var hoge = new Hoge();
hoge.someMethod2.innerMethod();

↑ の形で呼び出すと、js の規則として、innerMethod 内部からみた this は hoge.someMethod2 になりますね。

単純に innerMethod 内部の this が hoge を指す必要があるなら、私ならこう(↓)するかな~ と

var hoge = new Hoge();
hoge.someMethod2.innerMethod.call(hoge); // call(hoge)

でも、毎回こうしなきゃいけない構造( I/F )になってしまっているので、使う側が大変ですよね。これ。
なので、私なら、こんな(↓) 感じの構造で組みますね。

function Hoge() {}
Hoge.prototype = {
 method1: function() {}
 method2: function() { this.method1(); }
};
(new Hoge()).method2();

質問の意図と違っていたらごめんなさい ><

os0x

someMethod2はMethodという名前であるもに関わらず、実際は単なるオブジェクトですね。
個人的にはこれが混乱の元だと思います。
次のコードのように、(callなどを使わない場合)someMethod2のメソッド内でのthisは、someMethod2自身になるので、異なるインスタンス同士で同じオブジェクトを参照していて、嵌りやすいポイントが出来てしまっています。

なお、私はprototypeにsomeMethod2のようなオブジェクトを入れるコードを初めて見ました。正直なところ、一般的な書き方ではないと思います。

coppieee

prototypeでネストしたオブジェクトを入れるのは、私も見たことがなく、たぶん一般的ではないと思います。

2.の例では someMethodCollection がネストして親が参照できないという問題でしたね。
そういった場合は、someMethodCollectionを別のオブジェクトとして定義してあげて、SomeMethodCollectionのコンストラクタの引数に親のオブジェクトを渡してあげれば解決できるかと思います。

子のオブジェクトからは、this.parentで親が参照できます。

tsmallfield

あるメソッドから親オブジェクト(this)のそのまた親オブジェクトを参照できたらと思ったこと、
私もあります(笑)

任意のオブジェクトを_()に渡すと、
this._[n] で n番目の親を参照可能になるようにしてみました。
(this._[0]はthisと同じという仕様です)

fingaholic

皆様、
ご回答頂き有難うございます。
そうそうたる方々にご回答頂いて、正直ビクビクしながらですが
自分なりに理解した内容を返信をさせて頂きます。

/////////////////////////////////////////////////////////////////////////////////

kyo_agoさん

>fuga.someMethodCollection.innerMethod1のthisは
>fuga.someMethodCollectionになっており、
>fugaとは全く別のオブジェクトになっているためです。
>(JSの.はオブジェクトの継承関係ではなく、
>プロパティの参照をしていることに注意してください)

何となくですがイメージできました。
fuga.someMethodCollection.innerMethod1に関しては
単なるプロパティの参照としてfugaを使用しているが、
「fuga自体のオブジェクトとは無関係」、
という感じでしょうか?

ただサンプルコードのfuga.setInnerPropによって
someMethodCollectionのプロパティを変更が出来るので、
「子供からは無関係でも親からは無関係でない」、
という言葉(解釈)で宜しいでしょうか?

>こう行った場合の解決方法としては
>fuga.someMethodCollectionにfugaの参照を持たせて
>fuga.someMethodCollection.innerMethod1内からthis.fuga経由で
>参照のが一般的かもしれません。
>(が、これだと循環参照になるので普通はもうちょっと設計的な方法で
>innerMethod1からfugaの参照を避けるかもしれません)

これは実は目からウロコでした。
こちら非常に参考になりました。

/////////////////////////////////////////////////////////////////////////////////

uupaaさん

>でも、毎回こうしなきゃいけない構造( I/F )になってしまっているので、
>使う側が大変ですよね。これ。
>なので、私なら、こんな(↓) 感じの構造で組みますね。

やはりこういうprototypeにネストされたオブジェクトを入れる方法自体が
一般的でない感じですね。。。

>hoge.someMethod2.innerMethod.call(hoge);

thisの参照をhogeにする感じですね。
ただ毎回call(hoge)する設計自体が間違いだと認識してきました。。。
お恥ずかしい。。。

>質問の意図と違っていたらごめんなさい ><

とんでもないです!
通常設計しないような処理の質問にご回答頂いて感謝しております!

/////////////////////////////////////////////////////////////////////////////////

os0xさん

>someMethod2はMethodという名前であるもに関わらず、
>実際は単なるオブジェクトですね。
>個人的にはこれが混乱の元だと思います。

申し訳ないです。。。
そもそも設計がおかしい事を気づかせて頂きました。

>(callなどを使わない場合)someMethod2のメソッド内でのthisは、
>someMethod2自身になるので、
>異なるインスタンス同士で同じオブジェクトを参照していて、
>嵌りやすいポイントが出来てしまっています。

ご回答していただいたサンプルコード、
大変勉強になりました。
二つのインスタンスでthisの参照先が同じになっている点からも
子オブジェクトが親オブジェクトとは無関係にあるという事が分かりました。

/////////////////////////////////////////////////////////////////////////////////

coppieeeさん

>prototypeでネストしたオブジェクトを入れるのは、
>私も見たことがなく、たぶん一般的ではないと思います。

ですよね。。。
やっと設計が間違いだと確信致しました。

>someMethodCollectionを別のオブジェクトとして定義してあげて、
>SomeMethodCollectionのコンストラクタの引数に
>親のオブジェクトを渡してあげれば解決できるかと思います。

これが自分の中で一番しっくりくる解決方法でした。
この方法であればコードも分離できるので管理しやすいイメージです。
有難うございます!

/////////////////////////////////////////////////////////////////////////////////

tsmallfieldさん

>あるメソッドから親オブジェクト(this)のそのまた親オブジェクトを
>参照できたらと思ったこと、私もあります(笑)

自分だけでなくて安心しました。
ご質問させて頂いてよかったです。。。

_()をスニペット的に用意しておけば参照できる感じですね。
しかも指定した階層の親オブジェクトまで。。。

正直中身の処理については自分の力でまだ理解出来ない箇所もありますが
理解できるよう頑張ります!

/////////////////////////////////////////////////////////////////////////////////

自分的まとめ

・そもそもネストされたオブジェクトをprototypeに持つ設計は一般的でない。
・jsの「.」はオブジェクトの継承関係ではなく、プロパティの参照をしている。
・thisは深い
・jsdo.itすごい。

Post a question

You can post JavaScript, HTML, CSS related questions with attached reference to actual codes and get the answers likewise.

Ask a question

  • 1. Click the ‘Post a question’ above

  • 2. Enter question details, reference code, and click ‘Add question’ button

  • 3. Question will be paired with the code on display

Add an answer

  • 1. Choose a question

  • 2. Enter answer details, attach the reference code and click ‘add answer’ button

  • 3. Your answer will accompanied with attached code

Tag