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

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

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

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

Q&A

解決済

3回答

1730閲覧

クラスのメソッドで副作用を起こさないようにすることが難しい

wieee

総合スコア11

JavaScript

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

0グッド

0クリップ

投稿2020/12/08 15:01

編集2020/12/09 01:14

前提・実現したいこと

javascriptでクラスのメソッドを書いていますが、どうしても副作用を起こさないメソッドのコーディングが難しいです。

該当のソースコード

以下のようにプライベートメソッドthis._valueを更新するupdateValueメソッドがあります。

js

1class TestClass { 2 _value = null; 3 4 // this._valueを書き換えるメソッド 5 updateValue(value) { 6 this._value = value; 7 } 8}

このthis._valueを使って計算するメソッドcalcFuncを追加します

js

1class TestClass { 2 _value = null; 3 4 // this._valueを書き換えるメソッド 5 updateValue(value) { 6 this._value = value; 7 } 8 9 // 計算するメソッド 10 calcFunc(multipliedValue) { 11 return this._value * multipliedValue; 12 } 13}

質問詳細

この場合this._valueの値が変わるとcalcFuncメソッドの結果が変わってしまいます。
これは副作用を起こすメソッドということだと思うのですが、様々なサイトでは「関数の外で定義された変数によって結果が異なるので参照透過性がない」と書かれています。

js

1const test = new TestClass(); 2test.updateValue(2); 3test.calcFunc(3); // 6 4 5test.updateValue(3); 6test.calcFunc(3); // 9

この例のコードが簡単すぎてあまり良くない例かもしれません。
ですが、プライベート変数に値を保持しておいて、メソッドの戻り値がプライベート変数によって変わるのは副作用を起こすのでこれはダメなコーディングでしょうか?
副作用、参照透過性を解説しているサイトはどれも関数について書かれていて、クラスのメソッドについてではない気がしています。
もしメソッドが副作用を起こさないようにするにはプライベート変数を使うべきではないのでは?とも思ってしまいますがプライベート変数を使わないことができません。

どのようにしたらメソッドに副作用を起こさないように書けるのでしょうか?
どうぞ宜しくお願い致します。

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

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

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

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

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

Zuishin

2020/12/08 15:30

副作用と冪等性を混同していると思います。 参照透過にしたいなら次のように書いてください。しかし参照透過にしなければならないということはありません。 calcFunc(value, multipliedValue) { return value * multipliedValue; }
wieee

2020/12/09 01:20

ご回答ありがとうございます。 冪等性について自分の理解が間違っている可能性があるため質問内容から削除致しました。 > 参照透過にしたいなら次のように書いてください。しかし参照透過にしなければならないということはありません。 数ヶ月ほど副作用や参照透過性についての他サイトなどの記事を読んでいて、「関数内で副作用を起こすと結果が変わるために参照透過性を持たせたコーディングにすべき」のように書かれていて混乱しておりました。ずっと参照透過性を持たせないといけないと思い込んでいましたので、「しかし参照透過にしなければならないということはありません」というご回答から、安心感は出ましたがまだモヤモヤしております。
Zuishin

2020/12/09 01:51

TestClass をイミュータブルにし、updateValue を新しいオブジェクトを返す関数にすれば参照透過性は保たれますね。しかしイミュータブルにできる場合とできない場合があるので、原理主義的にこだわる必要はないと思います。Entity と ValueObject について調べてみてはどうでしょうか。
wieee

2020/12/10 01:43

再度ありがとうございます。 なるほど・・・いろいろなサイトの説明を見ていてずっと参照透過性にこだわりすぎていました。 「クラスの見通しが悪い」「メソッドの結果が予測できない」「メソッドのテストがしづらい」というところと現実的に参照透過性を維持するのは難しいので悩んでおりましたがこちらで質問してよかったです。 また、Entity と ValueObject についても調べてみようと思います。
guest

回答3

0

以下のようにプライベートメソッドthis._valueを更新するupdateValueメソッドがあります。

副作用を起こすことが目的のメソッドを、副作用を起こさないように書く事はできないのは自明では?

投稿2020/12/08 15:37

otn

総合スコア85901

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

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

wieee

2020/12/09 01:27

ご回答ありがとうございます。 > 副作用を起こすことが目的のメソッドを、副作用を起こさないように書く事はできないのは自明では? はい、副作用を起こさないように書くことができないのは自明なのですが、「引数を与えたら必ず同一の結果が返る」ようにコーディングするべきのように書かれているサイトが多いため、実際にどうすべきかわかりませんでした。 そのようなサイトでは「グローバル変数と関数」について書かれているため「クラスのプライベート変数とメソッド」には当てはまらないのでしょうか? もし全てのメソッドに参照透過性を持たせる場合、クラスの外で値を保持しておくしかない気がしてクラスの意味がないのでは?と思うようになりました。 「副作用を起こさない」=「メソッドの中で外の値を使って結果を出したり、または外の値を書き換えたりしない」ということがクラス内で実現できないので混乱している状況です。
otn

2020/12/09 11:59

「一度作成したオブジェクトは変更しない」という考え方があって、それに従うなら、そういうことになります。 ただ、DBを更新したり、ファイルに書いたりというメソッドは、副作用それ自体が目的なので、副作用の無いメソッドだけでシステムを構成するのは無理でしょう。 > 「引数を与えたら必ず同一の結果が返る」ようにコーディングするべきのように書かれているサイト のサイトをよく読んで、前提とか範囲を確認してはどうでしょうか?
wieee

2020/12/29 02:03

ありがとうございました。 おっしゃるとおりもう一度それらのサイトをよく読んで再確認してみます。
guest

0

この場合this._valueの値が変わるとcalcFuncメソッドの結果が変わってしまいます。
これは副作用を起こすメソッドということだと思うのですが、

「これ」がcalcFuncを指しているなら、違います。
このクラスの場合、updateValueは副作用を持ちますが、calcFuncは副作用を持ちません。
updateValueは変数testの内部状態を変化させるので副作用を持ちますが、calcFuncは変数testの内部状態(を含む、プログラム中のすべての内部状態)を変化させないので、副作用を持つとは言いません。

投稿2020/12/09 12:49

fukken

総合スコア73

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

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

wieee

2020/12/29 02:02

なるほど…。確かにそのとおりですね。 よく処理を確認しないと間違ってしまいますね。 根本的に勘違いしていたようなのでこちらで質問してよかったです。 非常に助かりました。
guest

0

ベストアンサー

副作用

this 値を変化させる関数は副作用があります。

全関数に参照透過性を持たせるなら、class を諦め、静的関数のみを使用する必要があります。
しかし、その為にはビルトイン関数のインスタンスメソッドを封印する必要があり、JavaScriptの大半の機能を犠牲にします。

スコープ

this 値のように、共有する事に意義がある値はスコープを最小限に抑える事が肝要と考えます。

もしメソッドが副作用を起こさないようにするにはプライベート変数を使うべきではないのでは?とも思ってしまいますがプライベート変数を使わないことができません。

そのコードはプライベートプロパティをコーディング規約で縛る実装であり、スコープは公開プロパティと変わりません。
Object.keys() でプライベートプロパティを列挙出来ますし、プライベートプロパティを参照させるべきではないメソッドからも参照出来ます。

比較論としては、Object.defineProperty で列挙不可能にしたり、WeakMap を使用してスコープを制限する実装がより好ましいと思います。

Re: wieee さん

投稿2020/12/09 23:33

編集2020/12/09 23:37
think49

総合スコア18189

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

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

wieee

2020/12/29 02:00 編集

ありがとうございます。 クラスのメソッドを参照透過性を持たせないといけないということ自体が勘違いをしておりました。 数々の副作用についての投稿や記事はクラスのメソッドに対しての記事ではなく関数に関しての記事だったようです。 また、Object.definePropertyでの列挙不可などのリンクありがとうございます。 参考にさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問