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

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

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

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

クロージャ

クロージャは、プログラミング言語における関数オブジェクトの一種です。 引数以外の変数を実行時の環境ではなく、 自身が定義された環境において解決することを特徴とします。

Q&A

解決済

3回答

2322閲覧

javascriptのクロージャでpublicなオブジェクトを変更したい

zpoket

総合スコア7

JavaScript

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

クロージャ

クロージャは、プログラミング言語における関数オブジェクトの一種です。 引数以外の変数を実行時の環境ではなく、 自身が定義された環境において解決することを特徴とします。

0グッド

0クリップ

投稿2016/11/26 10:30

編集2016/11/27 14:31

javascriptでのプログラミングを勉強しています。

グローバル変数の汚染対策として、モジュールの定義について調べて以下のようなコードを書いています。
まずモジュールを定義し、内部ではobjをpublicな変数として定義しました。その後objを書き換え、結果を出力してみました。

出力結果では、Hello.objもHello.getObj()も{"key": "newvalue"}が出力されることを期待していたのですが、Hello.getObj()は元のまま{"key": "value"}となっていてあれ??となってしまいました。

javascript

1// モジュールの定義 2var Hello = (function() { 3 var obj = {"key": "value"}; 4 var getObj = function() { 5 return obj; 6 }; 7 return {"obj": obj, "getObj": getObj}; 8}()); 9 10// publicな変数の変更 11Hello.obj = {"key": "newvalue"}; 12 13// 出力結果 14console.log(Hello.obj); // {"key": "newvalue"} 15console.log(Hello.getObj()); // {"key": "value"}

Helloモジュールのreturnでは、objが参照渡しで返されている??と思いますので、値のコピーを変更しているわけではないようです。
例えばモジュール定義の後でobjのキーを直接指定して変更すると、期待している動作になりました。

javascript

1// publicな変数の変更 2Hello.obj.key = "newvalue"; 3 4// 出力結果 5console.log(Hello.obj); // {"key": "newvalue"} 6console.log(Hello.getObj()); // {"key": "newvalue"}

ですので、最初のオブジェクトを上書きしてしまう方法では、モジュール内のobjと参照が切れてしまうということなのでしょうか??
また、参照が切れないようにobj変数をオブジェクトで上書きする方法はありますでしょうか??(setメソッドを用意すれば解決できると思いますが、イコールでオブジェクトを上書きできるように、モジュールを定義する方法があれば教えていただきたいです)

ご回答よろしくお願いいたします。

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

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

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

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

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

guest

回答3

0

Object.createObject.defineProperty{writable: false} を宣言すれば hello.obj の書き換えは禁止されると思います。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/create
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

getter がメソッド/プロパティの両方で参照出来るコードになっているので、どちらか一つに絞った方がわかりやすいと思います。

Re: zpoket さん

投稿2016/11/28 03:52

think49

総合スコア18162

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

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

zpoket

2016/11/28 14:44

ご回答ありがとうございます。 Hello.obj = {"key": "newvalue"}; とした後にHello.getObj()を実行したときに、{"key": "newvalue"}が返ったらよいなと思い、そのためにモジュール定義をどう修正すべきか疑問に思った次第です。(それを実現するために、Object.defineProperty で {writable: false}を設定するのが有効であるということだったのでしょうか??)
think49

2016/11/28 15:10 編集

zpoket さんは obj を参照する getter メソッドとして hello.getObj() を定義しました。 しかし、同じオブジェクトを hello.obj でも参照可能であり、これも getter として機能します。 hello.getObj() を使うなら hello.obj が不要であり、hello.obj を使うなら hello.getObj() が不要でしょう。 ですので、hello.getObj(), hello.obj のどちらか一つは削除して良い存在である、ということです。 Object.defineProperty は hello.obj の再代入による書き換えを禁止出来ます。 hello.getObj() は obj をクロージャに閉じこめることで obj の参照書き換えを禁止する事が可能ですが、hello.getObj() そのものを書き換える事で間接的に hello.getObj() の返り値を書き換える事が出来てしまいます。 これも Object.defineProperty を使うことで回避できます。
think49

2016/11/28 15:21

すみません、質問を誤解していたので Object.defineProperty の下りは忘れて下さい…。 ただ、やはり、hello.getObj() の存在異議が分かりません。 var hello = {obj: {key: 'value'}}; で十分なコードに読めます。
zpoket

2016/11/29 03:51

実際のコードでは、hello.getObj()を用意するつもりはなく、Hello.objでアクセスできればい良いです。Helloモジュールにある関数を定義し、その処理の中でHello.objを参照したとき、想定外のオブジェクトを参照していたため、それをシンプルに表現するためHello.getObj()を用意していました。(説明が不足していて、誤解を招いてしまって申し訳ありません‼︎)
guest

0

Helloに実行結果を入れるよりはnewしたほうがわかりやすいのでは?

javascript

1var Hello = new function() { 2 this.obj = {"key": "value"}; 3 this.getObj = function() { 4 return this.obj; 5 }; 6 this.setObj = function(arg) { 7 return this.obj=arg; 8 }; 9}(); 10console.log(Hello.obj); 11Hello.setObj({"key": "newvalue"}); 12console.log(Hello.obj); 13console.log(Hello.getObj());

投稿2016/11/28 02:31

yambejp

総合スコア114779

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

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

zpoket

2016/11/28 14:13

ご回答ありがとうございます。 newは使わないほうが良いという記事を見たことがあり避けていましたが、わかりやすさを考えると、yambejpさんの記述のほうが良いかもしれませんね。
guest

0

ベストアンサー

最初のオブジェクトを上書きしてしまう方法では、モジュール内のobjと参照が切れてしまうということなのでしょうか??

はい、そのとおりです。Hello.obj = {"key": "newvalue"};と書くと、リテラルで生成した新しいオブジェクトを代入することになり、元のobjとは別なオブジェクトになってしまいます。

参照が切れないようにobj変数をオブジェクトで上書きする方法はありますでしょうか??

いちばん手っ取り早いのは、getObj()Hello.objを返すようにしてしまう方法です(もっとも、それが意図したとおりの動作なのかわからない部分はありますが)。

投稿2016/11/26 10:50

maisumakun

総合スコア145183

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

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

zpoket

2016/11/27 13:10

ご回答ありがとうございます。 試行錯誤で動かしてみてはいるのですが、確証を得られずにいましたので助かりました。 getObj()がHello.objを返す方法というのは具体的な書き方として、 var getObj = function() {return this.obj;}; とgetObj()を定義するということでしょうか??(動作を確認したところ、意図している動作にはなっておりました!!) this.objとすることで、var objではなく、Helloモジュールで返されるオブジェクトの中のobjを、getObjが返してくれるようになったのかなと思っていますが、「this」をあまり使用したことがないため、このような書き方で逆に紛らわしいコードを書いていないかだけ心配でおります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問