JavaScriptのオブジェクトを扱うとき、=はただの値渡しではない
2015-01-29
検索で上の方にいたみたいなので、指摘でいただいている修正を下の方に書くのじゃなくて、上の方から直しました。
修正前の記事はこちらから。
JavaScriptのオブジェクトを扱うとき、=は値渡しではない - woshidan's loose leaf
今日の昼間、ちょちょいのちょいだぜー、と思っていた処理の実装がこれが原因で30分以上詰んだので、切なくなったから書く。
参考にしたのはここらへん。
JavaScript でオブジェクトや配列をコピーする方法 | monopocket.jp
JavaScript のオブジェクトは参照渡しで代入される | monopocket.jp
javascript - What is the most efficient way to clone an object? - Stack Overflow
=で値渡しされないの?
JavaScriptの場合、オブジェクトだと値渡しされない。
値渡しというのは不正確。値渡し自体はされている。ただし、オブジェクトの場合、値渡しされるのはポインタだ。参照の値渡しがされる。
参照とかを特に意識するのはC言語くらいで、それも値渡しをするには&とか特別な記号が必要だと思っていたので、参照を渡すことってあるんだ、と驚いた。
いや、あの、C言語だって、ポインタをプロパティに持つクラスのインスタンスを何かに代入したらポインタ自体が値渡しされたと思いますけれど。
で、=で参照の値渡しをされないオブジェクトですが、
いくつか忘れたとき用にオブジェクトの例を書いておくと、
- 日付,時間(Date,Timeオブジェクト)
- 配列,ハッシュ(=オブジェクト)
- 自分で作成したオブジェクト
例
で、
変数自体が値渡しされるのは
- 数字
- 文字列
- 真偽値
例
オブジェクトの参照の値渡しじゃなくて、オブジェクト自体の値渡しがしたい
オブジェクトの場合は参照の値渡しになってるから気をつけようね、じゃなくて値渡しオブジェクトの値渡しがしたい!
=で渡した後のオブジェクトをいじって、元のオブジェクトが変更されたくない!
という場合どうしたらいいか。
元オブジェクトの値を初期値とする新しいインスタンスを直接作る
コピーとはなんだったのか。
まあ、ガンバるのはブラウザだから、まあ……。
JSON.parse(JSON.stringify(obj))を使う
文字列が値渡しできるならとりあえず一回文字列にしちゃえばいいじゃない。
ただし、Dateオブジェクトはこれだとうまくいかない。
Jqueryの$.extend()を使う
既にJqueryを使っているなら、これが一番楽?
true => deep copyをする, false => shallow copyをする、の指定で、
これは空のオブジェクトにoriginal_dateのプロパティを追加している感じ。
でも同じ。こちらはoriginal_dateに空のオブジェクトのプロパティを追加という意味。
でもいけた。
これは空のオブジェクトを生成して、original_dataに空のオブジェクトのプロパティを追加したものを代入して返す、という意味。
自分には少し分かりにくかったので、例。
日付については、
というふうにこっちでも上手く行かなかったので、とりあえず、
がよさそう。
最近になって、なぜか妙にstubの書き方が少しだけ身にしみたのだけど、
使用するモデルの切り分けがうまく出来てなくて、結局ほとんど活用できていない感。
追記
shallow_copyではなく参照の値渡しです 両者の挙動は違います。
という指摘をいただきました。
配列などのオブジェクトで代入演算子の=を使うと、アドレスが渡されるのではなく、
アドレスの値が入った変数がコピーされる(参照の値渡し)ようです。
詳しくは、
Rubyist Magazine - 値渡しと参照渡しの違いを理解する
の参照の値渡しの項が分かりやすかったです。
というか、Rubyでも行われるのか。気をつけよう。
書いててもやもやしていた部分がすっきりしました。
jserさん、ありがとうございました。